@posthog/wizard 2.9.0 → 2.10.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 +40 -0
- package/dist/McpScreen-BmHapIaP.js +3357 -0
- package/dist/McpScreen-BmHapIaP.js.map +1 -0
- package/dist/add-mcp-server-to-clients-DZtISNrs.js +455 -0
- package/dist/add-mcp-server-to-clients-DZtISNrs.js.map +1 -0
- package/dist/agent-runner-CGwrcr57.js +1112 -0
- package/dist/agent-runner-CGwrcr57.js.map +1 -0
- package/dist/agent-skill-BVjJqol6.js +59 -0
- package/dist/agent-skill-BVjJqol6.js.map +1 -0
- package/dist/analytics-C4jO5Qda.js +207 -0
- package/dist/analytics-C4jO5Qda.js.map +1 -0
- package/dist/analytics-CpjaBpx6.js +2 -0
- package/dist/bin.d.ts +1 -2
- package/dist/bin.js +635 -524
- package/dist/bin.js.map +1 -1
- package/dist/craft-pre-release.sh +10 -0
- package/dist/debug-CIyf0ZGx.js +2 -0
- package/dist/debug-CyJ_3dTP.js +201 -0
- package/dist/debug-CyJ_3dTP.js.map +1 -0
- package/dist/defaults-DoVkE0gW.js +234 -0
- package/dist/defaults-DoVkE0gW.js.map +1 -0
- package/dist/detection-CkLpxBCD.js +122 -0
- package/dist/detection-CkLpxBCD.js.map +1 -0
- package/dist/env-api-key-K8TdTDII.js +20 -0
- package/dist/env-api-key-K8TdTDII.js.map +1 -0
- package/dist/file-utils-BWneZy6p.js +38 -0
- package/dist/file-utils-BWneZy6p.js.map +1 -0
- package/dist/generate-version.cjs +11 -0
- package/dist/package-json-BQgl5C3Z.js +2 -0
- package/dist/package-json-Ctq6LSl8.js +25 -0
- package/dist/package-json-Ctq6LSl8.js.map +1 -0
- package/dist/package-manager-nUQ-ebjr.js +222 -0
- package/dist/package-manager-nUQ-ebjr.js.map +1 -0
- package/dist/posthog-integration-BzxdDK4z.js +230 -0
- package/dist/posthog-integration-BzxdDK4z.js.map +1 -0
- package/dist/readiness-Dn7eq8NE.js +2156 -0
- package/dist/readiness-Dn7eq8NE.js.map +1 -0
- package/dist/registry-s55_iuJT.js +2922 -0
- package/dist/registry-s55_iuJT.js.map +1 -0
- package/dist/rolldown-runtime-B_-DWIq7.js +15 -0
- package/dist/router-D5A1Sb4p.js +141 -0
- package/dist/router-D5A1Sb4p.js.map +1 -0
- package/dist/setup-utils-CdDnllRW.js +928 -0
- package/dist/setup-utils-CdDnllRW.js.map +1 -0
- package/dist/smoke-test-ci.sh +165 -0
- package/dist/start-playground-B8qCLu7U.js +1005 -0
- package/dist/start-playground-B8qCLu7U.js.map +1 -0
- package/dist/start-tui-PygiIyNC.js +1887 -0
- package/dist/start-tui-PygiIyNC.js.map +1 -0
- package/dist/steps-CySv8XdD.js +146 -0
- package/dist/steps-CySv8XdD.js.map +1 -0
- package/dist/telemetry-Ct_GGkSO.js +13 -0
- package/dist/telemetry-Ct_GGkSO.js.map +1 -0
- package/dist/wizard-abort-7HUIsqv1.js +39 -0
- package/dist/wizard-abort-7HUIsqv1.js.map +1 -0
- package/dist/wizard-abort-Dhjb2o08.js +2 -0
- package/dist/wizard-session-Db6R023m.js +62 -0
- package/dist/wizard-session-Db6R023m.js.map +1 -0
- package/dist/wizard-session-y7nf6aKH.js +2 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +16 -8
- package/dist/src/__tests__/cli.test.d.ts +0 -1
- package/dist/src/__tests__/cli.test.js +0 -256
- package/dist/src/__tests__/cli.test.js.map +0 -1
- package/dist/src/__tests__/package-json.test.d.ts +0 -1
- package/dist/src/__tests__/package-json.test.js +0 -173
- package/dist/src/__tests__/package-json.test.js.map +0 -1
- package/dist/src/__tests__/wizard-abort.test.d.ts +0 -1
- package/dist/src/__tests__/wizard-abort.test.js +0 -148
- package/dist/src/__tests__/wizard-abort.test.js.map +0 -1
- package/dist/src/frameworks/android/android-wizard-agent.d.ts +0 -6
- package/dist/src/frameworks/android/android-wizard-agent.js +0 -142
- package/dist/src/frameworks/android/android-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/android/utils.d.ts +0 -11
- package/dist/src/frameworks/android/utils.js +0 -97
- package/dist/src/frameworks/android/utils.js.map +0 -1
- package/dist/src/frameworks/angular/angular-wizard-agent.d.ts +0 -4
- package/dist/src/frameworks/angular/angular-wizard-agent.js +0 -65
- package/dist/src/frameworks/angular/angular-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/angular/utils.d.ts +0 -4
- package/dist/src/frameworks/angular/utils.js +0 -9
- package/dist/src/frameworks/angular/utils.js.map +0 -1
- package/dist/src/frameworks/astro/astro-wizard-agent.d.ts +0 -7
- package/dist/src/frameworks/astro/astro-wizard-agent.js +0 -102
- package/dist/src/frameworks/astro/astro-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/astro/utils.d.ts +0 -14
- package/dist/src/frameworks/astro/utils.js +0 -114
- package/dist/src/frameworks/astro/utils.js.map +0 -1
- package/dist/src/frameworks/django/django-wizard-agent.d.ts +0 -8
- package/dist/src/frameworks/django/django-wizard-agent.js +0 -173
- package/dist/src/frameworks/django/django-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/django/utils.d.ts +0 -31
- package/dist/src/frameworks/django/utils.js +0 -306
- package/dist/src/frameworks/django/utils.js.map +0 -1
- package/dist/src/frameworks/fastapi/fastapi-wizard-agent.d.ts +0 -5
- package/dist/src/frameworks/fastapi/fastapi-wizard-agent.js +0 -191
- package/dist/src/frameworks/fastapi/fastapi-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/fastapi/utils.d.ts +0 -26
- package/dist/src/frameworks/fastapi/utils.js +0 -258
- package/dist/src/frameworks/fastapi/utils.js.map +0 -1
- package/dist/src/frameworks/flask/flask-wizard-agent.d.ts +0 -8
- package/dist/src/frameworks/flask/flask-wizard-agent.js +0 -179
- package/dist/src/frameworks/flask/flask-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/flask/utils.d.ts +0 -28
- package/dist/src/frameworks/flask/utils.js +0 -344
- package/dist/src/frameworks/flask/utils.js.map +0 -1
- package/dist/src/frameworks/javascript-node/javascript-node-wizard-agent.d.ts +0 -4
- package/dist/src/frameworks/javascript-node/javascript-node-wizard-agent.js +0 -61
- package/dist/src/frameworks/javascript-node/javascript-node-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/javascript-web/javascript-web-wizard-agent.d.ts +0 -3
- package/dist/src/frameworks/javascript-web/javascript-web-wizard-agent.js +0 -152
- package/dist/src/frameworks/javascript-web/javascript-web-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/javascript-web/utils.d.ts +0 -28
- package/dist/src/frameworks/javascript-web/utils.js +0 -147
- package/dist/src/frameworks/javascript-web/utils.js.map +0 -1
- package/dist/src/frameworks/laravel/laravel-wizard-agent.d.ts +0 -10
- package/dist/src/frameworks/laravel/laravel-wizard-agent.js +0 -182
- package/dist/src/frameworks/laravel/laravel-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/laravel/utils.d.ts +0 -38
- package/dist/src/frameworks/laravel/utils.js +0 -238
- package/dist/src/frameworks/laravel/utils.js.map +0 -1
- package/dist/src/frameworks/nextjs/nextjs-wizard-agent.d.ts +0 -7
- package/dist/src/frameworks/nextjs/nextjs-wizard-agent.js +0 -94
- package/dist/src/frameworks/nextjs/nextjs-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/nextjs/utils.d.ts +0 -12
- package/dist/src/frameworks/nextjs/utils.js +0 -52
- package/dist/src/frameworks/nextjs/utils.js.map +0 -1
- package/dist/src/frameworks/nuxt/nuxt-wizard-agent.d.ts +0 -6
- package/dist/src/frameworks/nuxt/nuxt-wizard-agent.js +0 -77
- package/dist/src/frameworks/nuxt/nuxt-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/python/python-wizard-agent.d.ts +0 -7
- package/dist/src/frameworks/python/python-wizard-agent.js +0 -195
- package/dist/src/frameworks/python/python-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/python/utils.d.ts +0 -28
- package/dist/src/frameworks/python/utils.js +0 -147
- package/dist/src/frameworks/python/utils.js.map +0 -1
- package/dist/src/frameworks/rails/rails-wizard-agent.d.ts +0 -8
- package/dist/src/frameworks/rails/rails-wizard-agent.js +0 -90
- package/dist/src/frameworks/rails/rails-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/rails/utils.d.ts +0 -37
- package/dist/src/frameworks/rails/utils.js +0 -187
- package/dist/src/frameworks/rails/utils.js.map +0 -1
- package/dist/src/frameworks/react-native/react-native-wizard-agent.d.ts +0 -7
- package/dist/src/frameworks/react-native/react-native-wizard-agent.js +0 -89
- package/dist/src/frameworks/react-native/react-native-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/react-native/utils.d.ts +0 -8
- package/dist/src/frameworks/react-native/utils.js +0 -28
- package/dist/src/frameworks/react-native/utils.js.map +0 -1
- package/dist/src/frameworks/react-router/react-router-wizard-agent.d.ts +0 -7
- package/dist/src/frameworks/react-router/react-router-wizard-agent.js +0 -94
- package/dist/src/frameworks/react-router/react-router-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/react-router/utils.d.ts +0 -13
- package/dist/src/frameworks/react-router/utils.js +0 -161
- package/dist/src/frameworks/react-router/utils.js.map +0 -1
- package/dist/src/frameworks/ruby/ruby-wizard-agent.d.ts +0 -7
- package/dist/src/frameworks/ruby/ruby-wizard-agent.js +0 -113
- package/dist/src/frameworks/ruby/ruby-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/ruby/utils.d.ts +0 -25
- package/dist/src/frameworks/ruby/utils.js +0 -158
- package/dist/src/frameworks/ruby/utils.js.map +0 -1
- package/dist/src/frameworks/svelte/svelte-wizard-agent.d.ts +0 -4
- package/dist/src/frameworks/svelte/svelte-wizard-agent.js +0 -61
- package/dist/src/frameworks/svelte/svelte-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/swift/swift-wizard-agent.d.ts +0 -7
- package/dist/src/frameworks/swift/swift-wizard-agent.js +0 -143
- package/dist/src/frameworks/swift/swift-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/swift/utils.d.ts +0 -8
- package/dist/src/frameworks/swift/utils.js +0 -105
- package/dist/src/frameworks/swift/utils.js.map +0 -1
- package/dist/src/frameworks/tanstack-router/tanstack-router-wizard-agent.d.ts +0 -7
- package/dist/src/frameworks/tanstack-router/tanstack-router-wizard-agent.js +0 -96
- package/dist/src/frameworks/tanstack-router/tanstack-router-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/tanstack-router/utils.d.ts +0 -11
- package/dist/src/frameworks/tanstack-router/utils.js +0 -149
- package/dist/src/frameworks/tanstack-router/utils.js.map +0 -1
- package/dist/src/frameworks/tanstack-start/tanstack-start-wizard-agent.d.ts +0 -4
- package/dist/src/frameworks/tanstack-start/tanstack-start-wizard-agent.js +0 -64
- package/dist/src/frameworks/tanstack-start/tanstack-start-wizard-agent.js.map +0 -1
- package/dist/src/frameworks/tanstack-start/utils.d.ts +0 -4
- package/dist/src/frameworks/tanstack-start/utils.js +0 -9
- package/dist/src/frameworks/tanstack-start/utils.js.map +0 -1
- package/dist/src/frameworks/vue/vue-wizard-agent.d.ts +0 -4
- package/dist/src/frameworks/vue/vue-wizard-agent.js +0 -62
- package/dist/src/frameworks/vue/vue-wizard-agent.js.map +0 -1
- package/dist/src/lib/__tests__/agent-interface.test.d.ts +0 -1
- package/dist/src/lib/__tests__/agent-interface.test.js +0 -317
- package/dist/src/lib/__tests__/agent-interface.test.js.map +0 -1
- package/dist/src/lib/__tests__/cloudflare-detection.test.d.ts +0 -1
- package/dist/src/lib/__tests__/cloudflare-detection.test.js +0 -119
- package/dist/src/lib/__tests__/cloudflare-detection.test.js.map +0 -1
- package/dist/src/lib/__tests__/wizard-tools.test.d.ts +0 -1
- package/dist/src/lib/__tests__/wizard-tools.test.js +0 -200
- package/dist/src/lib/__tests__/wizard-tools.test.js.map +0 -1
- package/dist/src/lib/__tests__/yara-hooks.test.d.ts +0 -1
- package/dist/src/lib/__tests__/yara-hooks.test.js +0 -432
- package/dist/src/lib/__tests__/yara-hooks.test.js.map +0 -1
- package/dist/src/lib/__tests__/yara-scanner.test.d.ts +0 -1
- package/dist/src/lib/__tests__/yara-scanner.test.js +0 -613
- package/dist/src/lib/__tests__/yara-scanner.test.js.map +0 -1
- package/dist/src/lib/agent/__tests__/agent-prompt.test.d.ts +0 -1
- package/dist/src/lib/agent/__tests__/agent-prompt.test.js +0 -57
- package/dist/src/lib/agent/__tests__/agent-prompt.test.js.map +0 -1
- package/dist/src/lib/agent/agent-interface.d.ts +0 -171
- package/dist/src/lib/agent/agent-interface.js +0 -1082
- package/dist/src/lib/agent/agent-interface.js.map +0 -1
- package/dist/src/lib/agent/agent-prompt.d.ts +0 -23
- package/dist/src/lib/agent/agent-prompt.js +0 -47
- package/dist/src/lib/agent/agent-prompt.js.map +0 -1
- package/dist/src/lib/agent/agent-runner.d.ts +0 -78
- package/dist/src/lib/agent/agent-runner.js +0 -323
- package/dist/src/lib/agent/agent-runner.js.map +0 -1
- package/dist/src/lib/agent/commandments.d.ts +0 -1
- package/dist/src/lib/agent/commandments.js +0 -26
- package/dist/src/lib/agent/commandments.js.map +0 -1
- package/dist/src/lib/api.d.ts +0 -75
- package/dist/src/lib/api.js +0 -105
- package/dist/src/lib/api.js.map +0 -1
- package/dist/src/lib/cloudflare-detection.d.ts +0 -14
- package/dist/src/lib/cloudflare-detection.js +0 -74
- package/dist/src/lib/cloudflare-detection.js.map +0 -1
- package/dist/src/lib/constants.d.ts +0 -74
- package/dist/src/lib/constants.js +0 -98
- package/dist/src/lib/constants.js.map +0 -1
- package/dist/src/lib/detection/__tests__/context.test.d.ts +0 -1
- package/dist/src/lib/detection/__tests__/context.test.js +0 -72
- package/dist/src/lib/detection/__tests__/context.test.js.map +0 -1
- package/dist/src/lib/detection/__tests__/features.test.d.ts +0 -1
- package/dist/src/lib/detection/__tests__/features.test.js +0 -75
- package/dist/src/lib/detection/__tests__/features.test.js.map +0 -1
- package/dist/src/lib/detection/__tests__/package-manager.test.d.ts +0 -1
- package/dist/src/lib/detection/__tests__/package-manager.test.js +0 -195
- package/dist/src/lib/detection/__tests__/package-manager.test.js.map +0 -1
- package/dist/src/lib/detection/context.d.ts +0 -31
- package/dist/src/lib/detection/context.js +0 -92
- package/dist/src/lib/detection/context.js.map +0 -1
- package/dist/src/lib/detection/features.d.ts +0 -16
- package/dist/src/lib/detection/features.js +0 -56
- package/dist/src/lib/detection/features.js.map +0 -1
- package/dist/src/lib/detection/framework.d.ts +0 -14
- package/dist/src/lib/detection/framework.js +0 -35
- package/dist/src/lib/detection/framework.js.map +0 -1
- package/dist/src/lib/detection/index.d.ts +0 -3
- package/dist/src/lib/detection/index.js +0 -11
- package/dist/src/lib/detection/index.js.map +0 -1
- package/dist/src/lib/detection/package-manager.d.ts +0 -37
- package/dist/src/lib/detection/package-manager.js +0 -183
- package/dist/src/lib/detection/package-manager.js.map +0 -1
- package/dist/src/lib/framework-config.d.ts +0 -175
- package/dist/src/lib/framework-config.js +0 -22
- package/dist/src/lib/framework-config.js.map +0 -1
- package/dist/src/lib/health-checks/__tests__/health-checks.test.d.ts +0 -18
- package/dist/src/lib/health-checks/__tests__/health-checks.test.js +0 -759
- package/dist/src/lib/health-checks/__tests__/health-checks.test.js.map +0 -1
- package/dist/src/lib/health-checks/endpoints.d.ts +0 -4
- package/dist/src/lib/health-checks/endpoints.js +0 -49
- package/dist/src/lib/health-checks/endpoints.js.map +0 -1
- package/dist/src/lib/health-checks/index.d.ts +0 -4
- package/dist/src/lib/health-checks/index.js +0 -24
- package/dist/src/lib/health-checks/index.js.map +0 -1
- package/dist/src/lib/health-checks/readiness.d.ts +0 -29
- package/dist/src/lib/health-checks/readiness.js +0 -188
- package/dist/src/lib/health-checks/readiness.js.map +0 -1
- package/dist/src/lib/health-checks/statuspage.d.ts +0 -9
- package/dist/src/lib/health-checks/statuspage.js +0 -105
- package/dist/src/lib/health-checks/statuspage.js.map +0 -1
- package/dist/src/lib/health-checks/types.d.ts +0 -32
- package/dist/src/lib/health-checks/types.js +0 -10
- package/dist/src/lib/health-checks/types.js.map +0 -1
- package/dist/src/lib/helper-functions.d.ts +0 -1
- package/dist/src/lib/helper-functions.js +0 -6
- package/dist/src/lib/helper-functions.js.map +0 -1
- package/dist/src/lib/middleware/benchmark.d.ts +0 -54
- package/dist/src/lib/middleware/benchmark.js +0 -45
- package/dist/src/lib/middleware/benchmark.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/cache-tracker.d.ts +0 -44
- package/dist/src/lib/middleware/benchmarks/cache-tracker.js +0 -81
- package/dist/src/lib/middleware/benchmarks/cache-tracker.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/compaction-tracker.d.ts +0 -29
- package/dist/src/lib/middleware/benchmarks/compaction-tracker.js +0 -60
- package/dist/src/lib/middleware/benchmarks/compaction-tracker.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/context-size-tracker.d.ts +0 -26
- package/dist/src/lib/middleware/benchmarks/context-size-tracker.js +0 -56
- package/dist/src/lib/middleware/benchmarks/context-size-tracker.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/cost-tracker.d.ts +0 -16
- package/dist/src/lib/middleware/benchmarks/cost-tracker.js +0 -76
- package/dist/src/lib/middleware/benchmarks/cost-tracker.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/duration-tracker.d.ts +0 -20
- package/dist/src/lib/middleware/benchmarks/duration-tracker.js +0 -40
- package/dist/src/lib/middleware/benchmarks/duration-tracker.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/index.d.ts +0 -9
- package/dist/src/lib/middleware/benchmarks/index.js +0 -60
- package/dist/src/lib/middleware/benchmarks/index.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/json-writer.d.ts +0 -15
- package/dist/src/lib/middleware/benchmarks/json-writer.js +0 -144
- package/dist/src/lib/middleware/benchmarks/json-writer.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/summary.d.ts +0 -9
- package/dist/src/lib/middleware/benchmarks/summary.js +0 -102
- package/dist/src/lib/middleware/benchmarks/summary.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/token-tracker.d.ts +0 -40
- package/dist/src/lib/middleware/benchmarks/token-tracker.js +0 -77
- package/dist/src/lib/middleware/benchmarks/token-tracker.js.map +0 -1
- package/dist/src/lib/middleware/benchmarks/turn-counter.d.ts +0 -34
- package/dist/src/lib/middleware/benchmarks/turn-counter.js +0 -59
- package/dist/src/lib/middleware/benchmarks/turn-counter.js.map +0 -1
- package/dist/src/lib/middleware/config.d.ts +0 -24
- package/dist/src/lib/middleware/config.js +0 -78
- package/dist/src/lib/middleware/config.js.map +0 -1
- package/dist/src/lib/middleware/index.d.ts +0 -11
- package/dist/src/lib/middleware/index.js +0 -18
- package/dist/src/lib/middleware/index.js.map +0 -1
- package/dist/src/lib/middleware/phase-detector.d.ts +0 -7
- package/dist/src/lib/middleware/phase-detector.js +0 -64
- package/dist/src/lib/middleware/phase-detector.js.map +0 -1
- package/dist/src/lib/middleware/pipeline.d.ts +0 -29
- package/dist/src/lib/middleware/pipeline.js +0 -82
- package/dist/src/lib/middleware/pipeline.js.map +0 -1
- package/dist/src/lib/middleware/types.d.ts +0 -40
- package/dist/src/lib/middleware/types.js +0 -9
- package/dist/src/lib/middleware/types.js.map +0 -1
- package/dist/src/lib/registry.d.ts +0 -3
- package/dist/src/lib/registry.js +0 -49
- package/dist/src/lib/registry.js.map +0 -1
- package/dist/src/lib/safe-tools.d.ts +0 -2
- package/dist/src/lib/safe-tools.js +0 -215
- package/dist/src/lib/safe-tools.js.map +0 -1
- package/dist/src/lib/skill-install.d.ts +0 -10
- package/dist/src/lib/skill-install.js +0 -23
- package/dist/src/lib/skill-install.js.map +0 -1
- package/dist/src/lib/version.d.ts +0 -1
- package/dist/src/lib/version.js +0 -6
- package/dist/src/lib/version.js.map +0 -1
- package/dist/src/lib/wizard-session.d.ts +0 -146
- package/dist/src/lib/wizard-session.js +0 -116
- package/dist/src/lib/wizard-session.js.map +0 -1
- package/dist/src/lib/wizard-tools.d.ts +0 -91
- package/dist/src/lib/wizard-tools.js +0 -389
- package/dist/src/lib/wizard-tools.js.map +0 -1
- package/dist/src/lib/workflows/__tests__/agent-skill.test.d.ts +0 -1
- package/dist/src/lib/workflows/__tests__/agent-skill.test.js +0 -66
- package/dist/src/lib/workflows/__tests__/agent-skill.test.js.map +0 -1
- package/dist/src/lib/workflows/__tests__/revenue-analytics-detect.test.d.ts +0 -1
- package/dist/src/lib/workflows/__tests__/revenue-analytics-detect.test.js +0 -101
- package/dist/src/lib/workflows/__tests__/revenue-analytics-detect.test.js.map +0 -1
- package/dist/src/lib/workflows/__tests__/workflow-registry.test.d.ts +0 -1
- package/dist/src/lib/workflows/__tests__/workflow-registry.test.js +0 -32
- package/dist/src/lib/workflows/__tests__/workflow-registry.test.js.map +0 -1
- package/dist/src/lib/workflows/__tests__/workflow-step.test.d.ts +0 -1
- package/dist/src/lib/workflows/__tests__/workflow-step.test.js +0 -54
- package/dist/src/lib/workflows/__tests__/workflow-step.test.js.map +0 -1
- package/dist/src/lib/workflows/agent-skill/index.d.ts +0 -44
- package/dist/src/lib/workflows/agent-skill/index.js +0 -47
- package/dist/src/lib/workflows/agent-skill/index.js.map +0 -1
- package/dist/src/lib/workflows/agent-skill/steps.d.ts +0 -8
- package/dist/src/lib/workflows/agent-skill/steps.js +0 -32
- package/dist/src/lib/workflows/agent-skill/steps.js.map +0 -1
- package/dist/src/lib/workflows/posthog-integration/detect.d.ts +0 -12
- package/dist/src/lib/workflows/posthog-integration/detect.js +0 -57
- package/dist/src/lib/workflows/posthog-integration/detect.js.map +0 -1
- package/dist/src/lib/workflows/posthog-integration/index.d.ts +0 -3
- package/dist/src/lib/workflows/posthog-integration/index.js +0 -152
- package/dist/src/lib/workflows/posthog-integration/index.js.map +0 -1
- package/dist/src/lib/workflows/posthog-integration/steps.d.ts +0 -9
- package/dist/src/lib/workflows/posthog-integration/steps.js +0 -100
- package/dist/src/lib/workflows/posthog-integration/steps.js.map +0 -1
- package/dist/src/lib/workflows/revenue-analytics/detect.d.ts +0 -40
- package/dist/src/lib/workflows/revenue-analytics/detect.js +0 -156
- package/dist/src/lib/workflows/revenue-analytics/detect.js.map +0 -1
- package/dist/src/lib/workflows/revenue-analytics/index.d.ts +0 -4
- package/dist/src/lib/workflows/revenue-analytics/index.js +0 -30
- package/dist/src/lib/workflows/revenue-analytics/index.js.map +0 -1
- package/dist/src/lib/workflows/revenue-analytics/steps.d.ts +0 -8
- package/dist/src/lib/workflows/revenue-analytics/steps.js +0 -48
- package/dist/src/lib/workflows/revenue-analytics/steps.js.map +0 -1
- package/dist/src/lib/workflows/workflow-registry.d.ts +0 -18
- package/dist/src/lib/workflows/workflow-registry.js +0 -32
- package/dist/src/lib/workflows/workflow-registry.js.map +0 -1
- package/dist/src/lib/workflows/workflow-step.d.ts +0 -126
- package/dist/src/lib/workflows/workflow-step.js +0 -28
- package/dist/src/lib/workflows/workflow-step.js.map +0 -1
- package/dist/src/lib/yara-hooks.d.ts +0 -44
- package/dist/src/lib/yara-hooks.js +0 -377
- package/dist/src/lib/yara-hooks.js.map +0 -1
- package/dist/src/lib/yara-scanner.d.ts +0 -61
- package/dist/src/lib/yara-scanner.js +0 -328
- package/dist/src/lib/yara-scanner.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/MCPClient.d.ts +0 -30
- package/dist/src/steps/add-mcp-server-to-clients/MCPClient.js +0 -138
- package/dist/src/steps/add-mcp-server-to-clients/MCPClient.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/__tests__/defaults.test.d.ts +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/__tests__/defaults.test.js +0 -72
- package/dist/src/steps/add-mcp-server-to-clients/__tests__/defaults.test.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/__tests__/claude.test.d.ts +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/__tests__/claude.test.js +0 -341
- package/dist/src/steps/add-mcp-server-to-clients/clients/__tests__/claude.test.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/__tests__/codex.test.d.ts +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/__tests__/codex.test.js +0 -108
- package/dist/src/steps/add-mcp-server-to-clients/clients/__tests__/codex.test.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/claude-code.d.ts +0 -89
- package/dist/src/steps/add-mcp-server-to-clients/clients/claude-code.js +0 -169
- package/dist/src/steps/add-mcp-server-to-clients/clients/claude-code.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/claude.d.ts +0 -80
- package/dist/src/steps/add-mcp-server-to-clients/clients/claude.js +0 -64
- package/dist/src/steps/add-mcp-server-to-clients/clients/claude.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/codex.d.ts +0 -88
- package/dist/src/steps/add-mcp-server-to-clients/clients/codex.js +0 -76
- package/dist/src/steps/add-mcp-server-to-clients/clients/codex.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/cursor.d.ts +0 -84
- package/dist/src/steps/add-mcp-server-to-clients/clients/cursor.js +0 -61
- package/dist/src/steps/add-mcp-server-to-clients/clients/cursor.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/visual-studio-code.d.ts +0 -92
- package/dist/src/steps/add-mcp-server-to-clients/clients/visual-studio-code.js +0 -101
- package/dist/src/steps/add-mcp-server-to-clients/clients/visual-studio-code.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/clients/zed.d.ts +0 -110
- package/dist/src/steps/add-mcp-server-to-clients/clients/zed.js +0 -102
- package/dist/src/steps/add-mcp-server-to-clients/clients/zed.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/defaults.d.ts +0 -119
- package/dist/src/steps/add-mcp-server-to-clients/defaults.js +0 -239
- package/dist/src/steps/add-mcp-server-to-clients/defaults.js.map +0 -1
- package/dist/src/steps/add-mcp-server-to-clients/index.d.ts +0 -23
- package/dist/src/steps/add-mcp-server-to-clients/index.js +0 -111
- package/dist/src/steps/add-mcp-server-to-clients/index.js.map +0 -1
- package/dist/src/steps/add-or-update-environment-variables.d.ts +0 -10
- package/dist/src/steps/add-or-update-environment-variables.js +0 -188
- package/dist/src/steps/add-or-update-environment-variables.js.map +0 -1
- package/dist/src/steps/index.d.ts +0 -4
- package/dist/src/steps/index.js +0 -21
- package/dist/src/steps/index.js.map +0 -1
- package/dist/src/steps/run-prettier.d.ts +0 -5
- package/dist/src/steps/run-prettier.js +0 -91
- package/dist/src/steps/run-prettier.js.map +0 -1
- package/dist/src/steps/upload-environment-variables/EnvironmentProvider.d.ts +0 -11
- package/dist/src/steps/upload-environment-variables/EnvironmentProvider.js +0 -12
- package/dist/src/steps/upload-environment-variables/EnvironmentProvider.js.map +0 -1
- package/dist/src/steps/upload-environment-variables/index.d.ts +0 -6
- package/dist/src/steps/upload-environment-variables/index.js +0 -38
- package/dist/src/steps/upload-environment-variables/index.js.map +0 -1
- package/dist/src/steps/upload-environment-variables/providers/__tests__/vercel.test.d.ts +0 -1
- package/dist/src/steps/upload-environment-variables/providers/__tests__/vercel.test.js +0 -117
- package/dist/src/steps/upload-environment-variables/providers/__tests__/vercel.test.js.map +0 -1
- package/dist/src/steps/upload-environment-variables/providers/vercel.d.ts +0 -15
- package/dist/src/steps/upload-environment-variables/providers/vercel.js +0 -142
- package/dist/src/steps/upload-environment-variables/providers/vercel.js.map +0 -1
- package/dist/src/telemetry.d.ts +0 -2
- package/dist/src/telemetry.js +0 -13
- package/dist/src/telemetry.js.map +0 -1
- package/dist/src/ui/index.d.ts +0 -8
- package/dist/src/ui/index.js +0 -17
- package/dist/src/ui/index.js.map +0 -1
- package/dist/src/ui/logging-ui.d.ts +0 -52
- package/dist/src/ui/logging-ui.js +0 -121
- package/dist/src/ui/logging-ui.js.map +0 -1
- package/dist/src/ui/tui/App.d.ts +0 -6
- package/dist/src/ui/tui/App.js +0 -10
- package/dist/src/ui/tui/App.js.map +0 -1
- package/dist/src/ui/tui/__tests__/flows.test.d.ts +0 -1
- package/dist/src/ui/tui/__tests__/flows.test.js +0 -115
- package/dist/src/ui/tui/__tests__/flows.test.js.map +0 -1
- package/dist/src/ui/tui/__tests__/layout-helpers.test.d.ts +0 -1
- package/dist/src/ui/tui/__tests__/layout-helpers.test.js +0 -68
- package/dist/src/ui/tui/__tests__/layout-helpers.test.js.map +0 -1
- package/dist/src/ui/tui/__tests__/router.test.d.ts +0 -1
- package/dist/src/ui/tui/__tests__/router.test.js +0 -87
- package/dist/src/ui/tui/__tests__/router.test.js.map +0 -1
- package/dist/src/ui/tui/__tests__/store.test.d.ts +0 -1
- package/dist/src/ui/tui/__tests__/store.test.js +0 -889
- package/dist/src/ui/tui/__tests__/store.test.js.map +0 -1
- package/dist/src/ui/tui/components/LearnCard.d.ts +0 -10
- package/dist/src/ui/tui/components/LearnCard.js +0 -217
- package/dist/src/ui/tui/components/LearnCard.js.map +0 -1
- package/dist/src/ui/tui/components/ServiceHealthList.d.ts +0 -15
- package/dist/src/ui/tui/components/ServiceHealthList.js +0 -57
- package/dist/src/ui/tui/components/ServiceHealthList.js.map +0 -1
- package/dist/src/ui/tui/components/TipsCard.d.ts +0 -9
- package/dist/src/ui/tui/components/TipsCard.js +0 -55
- package/dist/src/ui/tui/components/TipsCard.js.map +0 -1
- package/dist/src/ui/tui/components/TitleBar.d.ts +0 -6
- package/dist/src/ui/tui/components/TitleBar.js +0 -17
- package/dist/src/ui/tui/components/TitleBar.js.map +0 -1
- package/dist/src/ui/tui/flows.d.ts +0 -51
- package/dist/src/ui/tui/flows.js +0 -75
- package/dist/src/ui/tui/flows.js.map +0 -1
- package/dist/src/ui/tui/hooks/useStdoutDimensions.d.ts +0 -9
- package/dist/src/ui/tui/hooks/useStdoutDimensions.js +0 -37
- package/dist/src/ui/tui/hooks/useStdoutDimensions.js.map +0 -1
- package/dist/src/ui/tui/ink-ui.d.ts +0 -58
- package/dist/src/ui/tui/ink-ui.js +0 -125
- package/dist/src/ui/tui/ink-ui.js.map +0 -1
- package/dist/src/ui/tui/package.json +0 -1
- package/dist/src/ui/tui/playground/PlaygroundApp.d.ts +0 -12
- package/dist/src/ui/tui/playground/PlaygroundApp.js +0 -44
- package/dist/src/ui/tui/playground/PlaygroundApp.js.map +0 -1
- package/dist/src/ui/tui/playground/demos/HealthCheckDemo.d.ts +0 -11
- package/dist/src/ui/tui/playground/demos/HealthCheckDemo.js +0 -57
- package/dist/src/ui/tui/playground/demos/HealthCheckDemo.js.map +0 -1
- package/dist/src/ui/tui/playground/demos/InputDemo.d.ts +0 -4
- package/dist/src/ui/tui/playground/demos/InputDemo.js +0 -53
- package/dist/src/ui/tui/playground/demos/InputDemo.js.map +0 -1
- package/dist/src/ui/tui/playground/demos/LayoutDemo.d.ts +0 -5
- package/dist/src/ui/tui/playground/demos/LayoutDemo.js +0 -25
- package/dist/src/ui/tui/playground/demos/LayoutDemo.js.map +0 -1
- package/dist/src/ui/tui/playground/demos/LogDemo.d.ts +0 -5
- package/dist/src/ui/tui/playground/demos/LogDemo.js +0 -53
- package/dist/src/ui/tui/playground/demos/LogDemo.js.map +0 -1
- package/dist/src/ui/tui/playground/demos/ModalDemo.d.ts +0 -6
- package/dist/src/ui/tui/playground/demos/ModalDemo.js +0 -13
- package/dist/src/ui/tui/playground/demos/ModalDemo.js.map +0 -1
- package/dist/src/ui/tui/playground/demos/ProgressDemo.d.ts +0 -5
- package/dist/src/ui/tui/playground/demos/ProgressDemo.js +0 -58
- package/dist/src/ui/tui/playground/demos/ProgressDemo.js.map +0 -1
- package/dist/src/ui/tui/playground/demos/RunScreenDemo.d.ts +0 -11
- package/dist/src/ui/tui/playground/demos/RunScreenDemo.js +0 -159
- package/dist/src/ui/tui/playground/demos/RunScreenDemo.js.map +0 -1
- package/dist/src/ui/tui/playground/demos/WelcomeDemo.d.ts +0 -9
- package/dist/src/ui/tui/playground/demos/WelcomeDemo.js +0 -15
- package/dist/src/ui/tui/playground/demos/WelcomeDemo.js.map +0 -1
- package/dist/src/ui/tui/playground/start-playground.d.ts +0 -4
- package/dist/src/ui/tui/playground/start-playground.js +0 -24
- package/dist/src/ui/tui/playground/start-playground.js.map +0 -1
- package/dist/src/ui/tui/primitives/CardLayout.d.ts +0 -12
- package/dist/src/ui/tui/primitives/CardLayout.js +0 -10
- package/dist/src/ui/tui/primitives/CardLayout.js.map +0 -1
- package/dist/src/ui/tui/primitives/ConfirmationInput.d.ts +0 -13
- package/dist/src/ui/tui/primitives/ConfirmationInput.js +0 -35
- package/dist/src/ui/tui/primitives/ConfirmationInput.js.map +0 -1
- package/dist/src/ui/tui/primitives/ContentSequencer.d.ts +0 -42
- package/dist/src/ui/tui/primitives/ContentSequencer.js +0 -137
- package/dist/src/ui/tui/primitives/ContentSequencer.js.map +0 -1
- package/dist/src/ui/tui/primitives/DissolveTransition.d.ts +0 -21
- package/dist/src/ui/tui/primitives/DissolveTransition.js +0 -149
- package/dist/src/ui/tui/primitives/DissolveTransition.js.map +0 -1
- package/dist/src/ui/tui/primitives/Divider.d.ts +0 -6
- package/dist/src/ui/tui/primitives/Divider.js +0 -15
- package/dist/src/ui/tui/primitives/Divider.js.map +0 -1
- package/dist/src/ui/tui/primitives/EventPlanViewer.d.ts +0 -9
- package/dist/src/ui/tui/primitives/EventPlanViewer.js +0 -9
- package/dist/src/ui/tui/primitives/EventPlanViewer.js.map +0 -1
- package/dist/src/ui/tui/primitives/GroupedPickerMenu.d.ts +0 -23
- package/dist/src/ui/tui/primitives/GroupedPickerMenu.js +0 -182
- package/dist/src/ui/tui/primitives/GroupedPickerMenu.js.map +0 -1
- package/dist/src/ui/tui/primitives/HNViewer.d.ts +0 -7
- package/dist/src/ui/tui/primitives/HNViewer.js +0 -63
- package/dist/src/ui/tui/primitives/HNViewer.js.map +0 -1
- package/dist/src/ui/tui/primitives/LinesBlock.d.ts +0 -16
- package/dist/src/ui/tui/primitives/LinesBlock.js +0 -37
- package/dist/src/ui/tui/primitives/LinesBlock.js.map +0 -1
- package/dist/src/ui/tui/primitives/LoadingBox.d.ts +0 -8
- package/dist/src/ui/tui/primitives/LoadingBox.js +0 -10
- package/dist/src/ui/tui/primitives/LoadingBox.js.map +0 -1
- package/dist/src/ui/tui/primitives/LogViewer.d.ts +0 -11
- package/dist/src/ui/tui/primitives/LogViewer.js +0 -55
- package/dist/src/ui/tui/primitives/LogViewer.js.map +0 -1
- package/dist/src/ui/tui/primitives/ModalOverlay.d.ts +0 -25
- package/dist/src/ui/tui/primitives/ModalOverlay.js +0 -7
- package/dist/src/ui/tui/primitives/ModalOverlay.js.map +0 -1
- package/dist/src/ui/tui/primitives/NodeBlock.d.ts +0 -13
- package/dist/src/ui/tui/primitives/NodeBlock.js +0 -17
- package/dist/src/ui/tui/primitives/NodeBlock.js.map +0 -1
- package/dist/src/ui/tui/primitives/PickerMenu.d.ts +0 -20
- package/dist/src/ui/tui/primitives/PickerMenu.js +0 -143
- package/dist/src/ui/tui/primitives/PickerMenu.js.map +0 -1
- package/dist/src/ui/tui/primitives/ProgressList.d.ts +0 -17
- package/dist/src/ui/tui/primitives/ProgressList.js +0 -32
- package/dist/src/ui/tui/primitives/ProgressList.js.map +0 -1
- package/dist/src/ui/tui/primitives/PromptLabel.d.ts +0 -11
- package/dist/src/ui/tui/primitives/PromptLabel.js +0 -13
- package/dist/src/ui/tui/primitives/PromptLabel.js.map +0 -1
- package/dist/src/ui/tui/primitives/ScreenContainer.d.ts +0 -16
- package/dist/src/ui/tui/primitives/ScreenContainer.js +0 -36
- package/dist/src/ui/tui/primitives/ScreenContainer.js.map +0 -1
- package/dist/src/ui/tui/primitives/ScreenErrorBoundary.d.ts +0 -22
- package/dist/src/ui/tui/primitives/ScreenErrorBoundary.js +0 -35
- package/dist/src/ui/tui/primitives/ScreenErrorBoundary.js.map +0 -1
- package/dist/src/ui/tui/primitives/SplitView.d.ts +0 -11
- package/dist/src/ui/tui/primitives/SplitView.js +0 -9
- package/dist/src/ui/tui/primitives/SplitView.js.map +0 -1
- package/dist/src/ui/tui/primitives/TabContainer.d.ts +0 -23
- package/dist/src/ui/tui/primitives/TabContainer.js +0 -45
- package/dist/src/ui/tui/primitives/TabContainer.js.map +0 -1
- package/dist/src/ui/tui/primitives/TextBlock.d.ts +0 -41
- package/dist/src/ui/tui/primitives/TextBlock.js +0 -144
- package/dist/src/ui/tui/primitives/TextBlock.js.map +0 -1
- package/dist/src/ui/tui/primitives/content-types.d.ts +0 -37
- package/dist/src/ui/tui/primitives/content-types.js +0 -19
- package/dist/src/ui/tui/primitives/content-types.js.map +0 -1
- package/dist/src/ui/tui/primitives/index.d.ts +0 -27
- package/dist/src/ui/tui/primitives/index.js +0 -24
- package/dist/src/ui/tui/primitives/index.js.map +0 -1
- package/dist/src/ui/tui/primitives/layout-helpers.d.ts +0 -36
- package/dist/src/ui/tui/primitives/layout-helpers.js +0 -95
- package/dist/src/ui/tui/primitives/layout-helpers.js.map +0 -1
- package/dist/src/ui/tui/primitives/text-helpers.d.ts +0 -10
- package/dist/src/ui/tui/primitives/text-helpers.js +0 -43
- package/dist/src/ui/tui/primitives/text-helpers.js.map +0 -1
- package/dist/src/ui/tui/router.d.ts +0 -58
- package/dist/src/ui/tui/router.js +0 -96
- package/dist/src/ui/tui/router.js.map +0 -1
- package/dist/src/ui/tui/screen-registry.d.ts +0 -19
- package/dist/src/ui/tui/screen-registry.js +0 -44
- package/dist/src/ui/tui/screen-registry.js.map +0 -1
- package/dist/src/ui/tui/screens/AuthErrorScreen.d.ts +0 -7
- package/dist/src/ui/tui/screens/AuthErrorScreen.js +0 -16
- package/dist/src/ui/tui/screens/AuthErrorScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/AuthScreen.d.ts +0 -13
- package/dist/src/ui/tui/screens/AuthScreen.js +0 -20
- package/dist/src/ui/tui/screens/AuthScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/IntroScreen.d.ts +0 -16
- package/dist/src/ui/tui/screens/IntroScreen.js +0 -79
- package/dist/src/ui/tui/screens/IntroScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/ManagedSettingsScreen.d.ts +0 -13
- package/dist/src/ui/tui/screens/ManagedSettingsScreen.js +0 -32
- package/dist/src/ui/tui/screens/ManagedSettingsScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/McpScreen.d.ts +0 -24
- package/dist/src/ui/tui/screens/McpScreen.js +0 -127
- package/dist/src/ui/tui/screens/McpScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/OutroScreen.d.ts +0 -11
- package/dist/src/ui/tui/screens/OutroScreen.js +0 -22
- package/dist/src/ui/tui/screens/OutroScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/PortConflictScreen.d.ts +0 -11
- package/dist/src/ui/tui/screens/PortConflictScreen.js +0 -30
- package/dist/src/ui/tui/screens/PortConflictScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/RevenueIntroScreen.d.ts +0 -16
- package/dist/src/ui/tui/screens/RevenueIntroScreen.js +0 -64
- package/dist/src/ui/tui/screens/RevenueIntroScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/RunScreen.d.ts +0 -16
- package/dist/src/ui/tui/screens/RunScreen.js +0 -73
- package/dist/src/ui/tui/screens/RunScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/SettingsOverrideScreen.d.ts +0 -6
- package/dist/src/ui/tui/screens/SettingsOverrideScreen.js +0 -30
- package/dist/src/ui/tui/screens/SettingsOverrideScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/SetupScreen.d.ts +0 -13
- package/dist/src/ui/tui/screens/SetupScreen.js +0 -74
- package/dist/src/ui/tui/screens/SetupScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/SkillsScreen.d.ts +0 -14
- package/dist/src/ui/tui/screens/SkillsScreen.js +0 -71
- package/dist/src/ui/tui/screens/SkillsScreen.js.map +0 -1
- package/dist/src/ui/tui/screens/health/HealthCheckScreen.d.ts +0 -14
- package/dist/src/ui/tui/screens/health/HealthCheckScreen.js +0 -77
- package/dist/src/ui/tui/screens/health/HealthCheckScreen.js.map +0 -1
- package/dist/src/ui/tui/services/mcp-installer.d.ts +0 -21
- package/dist/src/ui/tui/services/mcp-installer.js +0 -58
- package/dist/src/ui/tui/services/mcp-installer.js.map +0 -1
- package/dist/src/ui/tui/start-tui.d.ts +0 -9
- package/dist/src/ui/tui/start-tui.js +0 -41
- package/dist/src/ui/tui/start-tui.js.map +0 -1
- package/dist/src/ui/tui/store.d.ts +0 -190
- package/dist/src/ui/tui/store.js +0 -472
- package/dist/src/ui/tui/store.js.map +0 -1
- package/dist/src/ui/tui/styles.d.ts +0 -32
- package/dist/src/ui/tui/styles.js +0 -35
- package/dist/src/ui/tui/styles.js.map +0 -1
- package/dist/src/ui/wizard-ui.d.ts +0 -81
- package/dist/src/ui/wizard-ui.js +0 -19
- package/dist/src/ui/wizard-ui.js.map +0 -1
- package/dist/src/utils/__tests__/analytics.test.d.ts +0 -1
- package/dist/src/utils/__tests__/analytics.test.js +0 -136
- package/dist/src/utils/__tests__/analytics.test.js.map +0 -1
- package/dist/src/utils/__tests__/provisioning.test.d.ts +0 -1
- package/dist/src/utils/__tests__/provisioning.test.js +0 -192
- package/dist/src/utils/__tests__/provisioning.test.js.map +0 -1
- package/dist/src/utils/__tests__/semver.test.d.ts +0 -1
- package/dist/src/utils/__tests__/semver.test.js +0 -159
- package/dist/src/utils/__tests__/semver.test.js.map +0 -1
- package/dist/src/utils/__tests__/setup-utils.test.d.ts +0 -1
- package/dist/src/utils/__tests__/setup-utils.test.js +0 -138
- package/dist/src/utils/__tests__/setup-utils.test.js.map +0 -1
- package/dist/src/utils/analytics.d.ts +0 -33
- package/dist/src/utils/analytics.js +0 -137
- package/dist/src/utils/analytics.js.map +0 -1
- package/dist/src/utils/anthropic-status.d.ts +0 -17
- package/dist/src/utils/anthropic-status.js +0 -49
- package/dist/src/utils/anthropic-status.js.map +0 -1
- package/dist/src/utils/bash.d.ts +0 -2
- package/dist/src/utils/bash.js +0 -54
- package/dist/src/utils/bash.js.map +0 -1
- package/dist/src/utils/custom-headers.d.ts +0 -9
- package/dist/src/utils/custom-headers.js +0 -24
- package/dist/src/utils/custom-headers.js.map +0 -1
- package/dist/src/utils/debug.d.ts +0 -29
- package/dist/src/utils/debug.js +0 -87
- package/dist/src/utils/debug.js.map +0 -1
- package/dist/src/utils/env-api-key.d.ts +0 -5
- package/dist/src/utils/env-api-key.js +0 -57
- package/dist/src/utils/env-api-key.js.map +0 -1
- package/dist/src/utils/environment.d.ts +0 -4
- package/dist/src/utils/environment.js +0 -77
- package/dist/src/utils/environment.js.map +0 -1
- package/dist/src/utils/file-utils.d.ts +0 -10
- package/dist/src/utils/file-utils.js +0 -49
- package/dist/src/utils/file-utils.js.map +0 -1
- package/dist/src/utils/logging.d.ts +0 -9
- package/dist/src/utils/logging.js +0 -47
- package/dist/src/utils/logging.js.map +0 -1
- package/dist/src/utils/oauth.d.ts +0 -33
- package/dist/src/utils/oauth.js +0 -288
- package/dist/src/utils/oauth.js.map +0 -1
- package/dist/src/utils/package-json.d.ts +0 -30
- package/dist/src/utils/package-json.js +0 -47
- package/dist/src/utils/package-json.js.map +0 -1
- package/dist/src/utils/package-manager.d.ts +0 -21
- package/dist/src/utils/package-manager.js +0 -210
- package/dist/src/utils/package-manager.js.map +0 -1
- package/dist/src/utils/provisioning.d.ts +0 -25
- package/dist/src/utils/provisioning.js +0 -191
- package/dist/src/utils/provisioning.js.map +0 -1
- package/dist/src/utils/rules/astro-rules.md +0 -44
- package/dist/src/utils/rules/next-rules.md +0 -9
- package/dist/src/utils/rules/react-native-rules.md +0 -7
- package/dist/src/utils/rules/react-rules.md +0 -7
- package/dist/src/utils/rules/svelte-rules.md +0 -7
- package/dist/src/utils/rules/universal.md +0 -32
- package/dist/src/utils/semver.d.ts +0 -21
- package/dist/src/utils/semver.js +0 -87
- package/dist/src/utils/semver.js.map +0 -1
- package/dist/src/utils/setup-utils.d.ts +0 -79
- package/dist/src/utils/setup-utils.js +0 -406
- package/dist/src/utils/setup-utils.js.map +0 -1
- package/dist/src/utils/string.d.ts +0 -1
- package/dist/src/utils/string.js +0 -9
- package/dist/src/utils/string.js.map +0 -1
- package/dist/src/utils/types.d.ts +0 -85
- package/dist/src/utils/types.js +0 -3
- package/dist/src/utils/types.js.map +0 -1
- package/dist/src/utils/urls.d.ts +0 -7
- package/dist/src/utils/urls.js +0 -78
- package/dist/src/utils/urls.js.map +0 -1
- package/dist/src/utils/vendor/is-unicorn-supported.d.ts +0 -1
- package/dist/src/utils/vendor/is-unicorn-supported.js +0 -24
- package/dist/src/utils/vendor/is-unicorn-supported.js.map +0 -1
- package/dist/src/utils/wizard-abort.d.ts +0 -16
- package/dist/src/utils/wizard-abort.js +0 -59
- package/dist/src/utils/wizard-abort.js.map +0 -1
|
@@ -0,0 +1,3357 @@
|
|
|
1
|
+
import { n as analytics, r as sessionProperties } from "./analytics-C4jO5Qda.js";
|
|
2
|
+
import { r as buildSession } from "./wizard-session-Db6R023m.js";
|
|
3
|
+
import { t as SERVICE_LABELS } from "./readiness-Dn7eq8NE.js";
|
|
4
|
+
import { n as AVAILABLE_FEATURES, t as ALL_FEATURE_VALUES } from "./defaults-DoVkE0gW.js";
|
|
5
|
+
import { i as WORKFLOW_STEPS, t as WizardRouter } from "./router-D5A1Sb4p.js";
|
|
6
|
+
import * as fs$1 from "fs";
|
|
7
|
+
import { Box, Text, measureElement, useInput, useStdout } from "ink";
|
|
8
|
+
import { Component, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
9
|
+
import { atom, map } from "nanostores";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { Spinner } from "@inkjs/ui";
|
|
12
|
+
//#region src/ui/tui/store.ts
|
|
13
|
+
/**
|
|
14
|
+
* WizardStore — Nanostore-backed reactive store for the TUI.
|
|
15
|
+
* React components subscribe via useSyncExternalStore.
|
|
16
|
+
*
|
|
17
|
+
* The active screen is derived from session state — WizardRouter walks
|
|
18
|
+
* the flow and shows the first step whose `isComplete` is still false.
|
|
19
|
+
*
|
|
20
|
+
* Define a step `gate` if your screen needs to await user interactions.
|
|
21
|
+
* bin.ts calls `await store.getGate(stepId)` to pause until the gate
|
|
22
|
+
* predicate becomes true.
|
|
23
|
+
*
|
|
24
|
+
* All session mutations that affect screen resolution go through
|
|
25
|
+
* explicit setters so emitChange() is always called.
|
|
26
|
+
*/
|
|
27
|
+
var WizardStore = class {
|
|
28
|
+
$session = map(buildSession({}));
|
|
29
|
+
$statusMessages = atom([]);
|
|
30
|
+
$statusExpanded = atom(false);
|
|
31
|
+
$tasks = atom([]);
|
|
32
|
+
$eventPlan = atom([]);
|
|
33
|
+
$learnCardBlockIdx = atom(0);
|
|
34
|
+
$learnCardComplete = atom(false);
|
|
35
|
+
$version = atom(0);
|
|
36
|
+
/** Last screen seen — used to detect screen transitions for analytics. */
|
|
37
|
+
_lastScreen = null;
|
|
38
|
+
/** Hooks run when transitioning onto a screen. */
|
|
39
|
+
_enterScreenHooks = /* @__PURE__ */ new Map();
|
|
40
|
+
/** Gate promises derived from workflow step definitions. */
|
|
41
|
+
_gates = /* @__PURE__ */ new Map();
|
|
42
|
+
version = "";
|
|
43
|
+
/** Navigation router — resolves active screen from session state. */
|
|
44
|
+
router;
|
|
45
|
+
/** Blocks agent execution until the settings-override overlay is dismissed. */
|
|
46
|
+
_resolveSettingsOverride = null;
|
|
47
|
+
_backupAndFixSettings = null;
|
|
48
|
+
/** Blocks OAuth flow until the port-conflict overlay is dismissed. */
|
|
49
|
+
_resolvePortConflict = null;
|
|
50
|
+
constructor(flow = "posthog-integration") {
|
|
51
|
+
this.router = new WizardRouter(flow);
|
|
52
|
+
this._initFromWorkflow(flow);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Scan workflow steps for gate predicates and onInit callbacks.
|
|
56
|
+
* Creates gate promises and fires init work.
|
|
57
|
+
*/
|
|
58
|
+
_initFromWorkflow(flow) {
|
|
59
|
+
const steps = WORKFLOW_STEPS[flow];
|
|
60
|
+
if (!steps) return;
|
|
61
|
+
for (const step of steps) if (step.gate) {
|
|
62
|
+
let resolve;
|
|
63
|
+
const promise = new Promise((r) => {
|
|
64
|
+
resolve = r;
|
|
65
|
+
});
|
|
66
|
+
this._gates.set(step.id, {
|
|
67
|
+
predicate: step.gate,
|
|
68
|
+
promise,
|
|
69
|
+
resolve,
|
|
70
|
+
resolved: false
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const getSession = () => this.session;
|
|
74
|
+
const ctx = {
|
|
75
|
+
get session() {
|
|
76
|
+
return getSession();
|
|
77
|
+
},
|
|
78
|
+
setReadinessResult: (r) => this.setReadinessResult(r),
|
|
79
|
+
setFrameworkContext: (k, v) => this.setFrameworkContext(k, v),
|
|
80
|
+
emitChange: () => this.emitChange()
|
|
81
|
+
};
|
|
82
|
+
for (const step of steps) step.onInit?.(ctx);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Run all `onReady` hooks declared by the current flow's steps, in
|
|
86
|
+
* order. Must be called after `store.session = session` so hooks see
|
|
87
|
+
* the real installDir. bin.ts calls this generically — it doesn't
|
|
88
|
+
* need to know which workflow has which pre-flow work.
|
|
89
|
+
*/
|
|
90
|
+
async runReadyHooks() {
|
|
91
|
+
const steps = WORKFLOW_STEPS[this.router.activeFlow];
|
|
92
|
+
if (!steps) return;
|
|
93
|
+
const ctx = {
|
|
94
|
+
session: this.session,
|
|
95
|
+
setFrameworkContext: (k, v) => this.setFrameworkContext(k, v),
|
|
96
|
+
setFrameworkConfig: (i, c) => this.setFrameworkConfig(i, c),
|
|
97
|
+
setDetectedFramework: (l) => this.setDetectedFramework(l),
|
|
98
|
+
setUnsupportedVersion: (info) => this.setUnsupportedVersion(info),
|
|
99
|
+
addDiscoveredFeature: (f) => this.addDiscoveredFeature(f),
|
|
100
|
+
setDetectionComplete: () => this.setDetectionComplete()
|
|
101
|
+
};
|
|
102
|
+
for (const step of steps) if (step.onReady) await step.onReady(ctx);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get a gate promise by step ID — the primary blocking checkpoint API
|
|
106
|
+
* for bin.ts. `await store.getGate('...')` parks the caller until the
|
|
107
|
+
* corresponding workflow step's gate predicate flips to true (if the
|
|
108
|
+
* predicate stays false, the caller stays parked indefinitely — the
|
|
109
|
+
* TUI keeps rendering so the user can resolve whatever is blocking).
|
|
110
|
+
*
|
|
111
|
+
* If the workflow doesn't define a step with this ID, or the step
|
|
112
|
+
* has no `gate` predicate, this returns an already-resolved promise
|
|
113
|
+
* so bin.ts flows straight through. This lets workflows opt in to
|
|
114
|
+
* gates on a per-step basis without bin.ts needing to know which
|
|
115
|
+
* gates exist in which flow.
|
|
116
|
+
*/
|
|
117
|
+
getGate(stepId) {
|
|
118
|
+
return this._gates.get(stepId)?.promise ?? Promise.resolve();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Re-evaluate every gate predicate against the current session and
|
|
122
|
+
* resolve any whose predicate now returns true. Called after every
|
|
123
|
+
* emitChange(), so gates unblock as soon as the session mutation
|
|
124
|
+
* that satisfies them lands. Gates only resolve once — a predicate
|
|
125
|
+
* that goes true → false → true will NOT re-block a caller that
|
|
126
|
+
* already awaited through.
|
|
127
|
+
*/
|
|
128
|
+
_checkGates() {
|
|
129
|
+
for (const [, gate] of this._gates) if (!gate.resolved && gate.predicate(this.session)) {
|
|
130
|
+
gate.resolved = true;
|
|
131
|
+
gate.resolve();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
get session() {
|
|
135
|
+
return this.$session.get();
|
|
136
|
+
}
|
|
137
|
+
set session(value) {
|
|
138
|
+
this.$session.set(value);
|
|
139
|
+
this.emitChange();
|
|
140
|
+
}
|
|
141
|
+
get statusMessages() {
|
|
142
|
+
return this.$statusMessages.get();
|
|
143
|
+
}
|
|
144
|
+
get tasks() {
|
|
145
|
+
return this.$tasks.get();
|
|
146
|
+
}
|
|
147
|
+
get eventPlan() {
|
|
148
|
+
return this.$eventPlan.get();
|
|
149
|
+
}
|
|
150
|
+
get statusExpanded() {
|
|
151
|
+
return this.$statusExpanded.get();
|
|
152
|
+
}
|
|
153
|
+
toggleStatusExpanded() {
|
|
154
|
+
this.$statusExpanded.set(!this.$statusExpanded.get());
|
|
155
|
+
this.emitChange();
|
|
156
|
+
}
|
|
157
|
+
setStatusExpanded(expanded) {
|
|
158
|
+
if (this.$statusExpanded.get() !== expanded) {
|
|
159
|
+
this.$statusExpanded.set(expanded);
|
|
160
|
+
this.emitChange();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** Sets setupConfirmed. Gate resolves via _checkGates(). */
|
|
164
|
+
completeSetup() {
|
|
165
|
+
this.$session.setKey("setupConfirmed", true);
|
|
166
|
+
analytics.wizardCapture("setup confirmed", sessionProperties(this.session));
|
|
167
|
+
this.emitChange();
|
|
168
|
+
}
|
|
169
|
+
setRunPhase(phase) {
|
|
170
|
+
this.$session.setKey("runPhase", phase);
|
|
171
|
+
this.emitChange();
|
|
172
|
+
}
|
|
173
|
+
setCredentials(credentials) {
|
|
174
|
+
this.$session.setKey("credentials", credentials);
|
|
175
|
+
analytics.wizardCapture("auth complete", { project_id: credentials?.projectId });
|
|
176
|
+
this.emitChange();
|
|
177
|
+
}
|
|
178
|
+
setFrameworkConfig(integration, config) {
|
|
179
|
+
this.$session.setKey("integration", integration);
|
|
180
|
+
this.$session.setKey("frameworkConfig", config);
|
|
181
|
+
this.$session.setKey("unsupportedVersion", null);
|
|
182
|
+
this.emitChange();
|
|
183
|
+
}
|
|
184
|
+
setDetectionComplete() {
|
|
185
|
+
this.$session.setKey("detectionComplete", true);
|
|
186
|
+
this.emitChange();
|
|
187
|
+
}
|
|
188
|
+
setDetectedFramework(label) {
|
|
189
|
+
this.$session.setKey("detectedFrameworkLabel", label);
|
|
190
|
+
this.emitChange();
|
|
191
|
+
}
|
|
192
|
+
setUnsupportedVersion(info) {
|
|
193
|
+
this.$session.setKey("unsupportedVersion", info);
|
|
194
|
+
this.emitChange();
|
|
195
|
+
}
|
|
196
|
+
setLoginUrl(url) {
|
|
197
|
+
this.$session.setKey("loginUrl", url);
|
|
198
|
+
this.emitChange();
|
|
199
|
+
}
|
|
200
|
+
setReadinessResult(result) {
|
|
201
|
+
this.$session.setKey("readinessResult", result);
|
|
202
|
+
this.emitChange();
|
|
203
|
+
}
|
|
204
|
+
/** User dismissed the blocking outage screen. Gate resolves via _checkGates(). */
|
|
205
|
+
dismissOutage() {
|
|
206
|
+
this.$session.setKey("outageDismissed", true);
|
|
207
|
+
this.emitChange();
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Push the settings-override overlay and return a promise that blocks
|
|
211
|
+
* until the user dismisses it via backupAndFixSettingsOverride().
|
|
212
|
+
*/
|
|
213
|
+
showSettingsOverride(conflicts, backupAndFix) {
|
|
214
|
+
const allKeys = conflicts.flatMap((c) => c.keys);
|
|
215
|
+
this.$session.setKey("settingsOverrideKeys", allKeys);
|
|
216
|
+
this.$session.setKey("settingsConflicts", conflicts);
|
|
217
|
+
this._backupAndFixSettings = backupAndFix;
|
|
218
|
+
if (conflicts.some((c) => !c.writable)) this.pushOverlay("managed-settings");
|
|
219
|
+
else this.pushOverlay("settings-override");
|
|
220
|
+
return new Promise((resolve) => {
|
|
221
|
+
this._resolveSettingsOverride = resolve;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Push the port-conflict overlay and return a promise that blocks
|
|
226
|
+
* until the user frees the ports and retries, or exits.
|
|
227
|
+
*/
|
|
228
|
+
showPortConflict(processInfo) {
|
|
229
|
+
this.$session.setKey("portConflictProcess", processInfo);
|
|
230
|
+
this.pushOverlay("port-conflict");
|
|
231
|
+
return new Promise((resolve) => {
|
|
232
|
+
this._resolvePortConflict = resolve;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/** Dismiss the port-conflict overlay and retry the OAuth port loop. */
|
|
236
|
+
resolvePortConflict() {
|
|
237
|
+
this.$session.setKey("portConflictProcess", null);
|
|
238
|
+
this.popOverlay();
|
|
239
|
+
this._resolvePortConflict?.();
|
|
240
|
+
this._resolvePortConflict = null;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Back up .claude/settings.json. Dismisses the overlay on success.
|
|
244
|
+
*/
|
|
245
|
+
backupAndFixSettingsOverride() {
|
|
246
|
+
const ok = this._backupAndFixSettings?.() ?? false;
|
|
247
|
+
if (ok) {
|
|
248
|
+
this.$session.setKey("settingsOverrideKeys", null);
|
|
249
|
+
this.$session.setKey("settingsConflicts", null);
|
|
250
|
+
this.popOverlay();
|
|
251
|
+
this._resolveSettingsOverride?.();
|
|
252
|
+
this._resolveSettingsOverride = null;
|
|
253
|
+
this._backupAndFixSettings = null;
|
|
254
|
+
}
|
|
255
|
+
return ok;
|
|
256
|
+
}
|
|
257
|
+
/** Push the auth-error overlay (no dismiss — user must exit). */
|
|
258
|
+
showAuthError() {
|
|
259
|
+
this.pushOverlay("auth-error");
|
|
260
|
+
}
|
|
261
|
+
addDiscoveredFeature(feature) {
|
|
262
|
+
if (!this.session.discoveredFeatures.includes(feature)) {
|
|
263
|
+
this.session.discoveredFeatures.push(feature);
|
|
264
|
+
this.emitChange();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Enable an additional feature: enqueue it for the stop hook
|
|
269
|
+
* and set any feature-specific session flags.
|
|
270
|
+
*/
|
|
271
|
+
enableFeature(feature) {
|
|
272
|
+
if (!this.session.additionalFeatureQueue.includes(feature)) this.session.additionalFeatureQueue.push(feature);
|
|
273
|
+
if (feature === "llm") this.session.llmOptIn = true;
|
|
274
|
+
analytics.wizardCapture("feature enabled", { feature });
|
|
275
|
+
this.emitChange();
|
|
276
|
+
}
|
|
277
|
+
setMcpComplete(outcome = "skipped", installedClients = []) {
|
|
278
|
+
this.$session.setKey("mcpComplete", true);
|
|
279
|
+
this.$session.setKey("mcpOutcome", outcome);
|
|
280
|
+
this.$session.setKey("mcpInstalledClients", installedClients);
|
|
281
|
+
analytics.wizardCapture("mcp complete", {
|
|
282
|
+
mcp_outcome: outcome,
|
|
283
|
+
mcp_installed_clients: installedClients,
|
|
284
|
+
...sessionProperties(this.session)
|
|
285
|
+
});
|
|
286
|
+
this.emitChange();
|
|
287
|
+
}
|
|
288
|
+
setSkillsComplete(kept) {
|
|
289
|
+
this.$session.setKey("skillsComplete", true);
|
|
290
|
+
analytics.wizardCapture("skills complete", {
|
|
291
|
+
skills_kept: kept,
|
|
292
|
+
...sessionProperties(this.session)
|
|
293
|
+
});
|
|
294
|
+
this.emitChange();
|
|
295
|
+
}
|
|
296
|
+
setOutroDismissed() {
|
|
297
|
+
this.$session.setKey("outroDismissed", true);
|
|
298
|
+
this.emitChange();
|
|
299
|
+
}
|
|
300
|
+
setOutroData(data) {
|
|
301
|
+
this.$session.setKey("outroData", data);
|
|
302
|
+
this.emitChange();
|
|
303
|
+
}
|
|
304
|
+
setFrameworkContext(key, value) {
|
|
305
|
+
const ctx = {
|
|
306
|
+
...this.$session.get().frameworkContext,
|
|
307
|
+
[key]: value
|
|
308
|
+
};
|
|
309
|
+
this.$session.setKey("frameworkContext", ctx);
|
|
310
|
+
this.emitChange();
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* The screen that should be rendered right now.
|
|
314
|
+
* Derived from session state via the router.
|
|
315
|
+
*/
|
|
316
|
+
get currentScreen() {
|
|
317
|
+
return this.router.resolve(this.session);
|
|
318
|
+
}
|
|
319
|
+
/** Direction hint for screen transitions. */
|
|
320
|
+
get lastNavDirection() {
|
|
321
|
+
return this.router.lastNavDirection;
|
|
322
|
+
}
|
|
323
|
+
getVersion() {
|
|
324
|
+
return this.$version.get();
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Notify React that state has changed.
|
|
328
|
+
* The router re-resolves the active screen on next render.
|
|
329
|
+
* Gate predicates are checked and resolved if ready.
|
|
330
|
+
*/
|
|
331
|
+
emitChange() {
|
|
332
|
+
this.router._setDirection("push");
|
|
333
|
+
this.$version.set(this.$version.get() + 1);
|
|
334
|
+
this._checkGates();
|
|
335
|
+
this._detectTransition();
|
|
336
|
+
}
|
|
337
|
+
pushOverlay(overlay) {
|
|
338
|
+
this.router._setDirection("push");
|
|
339
|
+
this.router.pushOverlay(overlay);
|
|
340
|
+
this.$version.set(this.$version.get() + 1);
|
|
341
|
+
this._detectTransition();
|
|
342
|
+
}
|
|
343
|
+
popOverlay() {
|
|
344
|
+
this.router._setDirection("pop");
|
|
345
|
+
this.router.popOverlay();
|
|
346
|
+
this.$version.set(this.$version.get() + 1);
|
|
347
|
+
this._detectTransition();
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Register a callback to run when transitioning onto the given screen.
|
|
351
|
+
* Fires after every transition that lands on this screen.
|
|
352
|
+
*/
|
|
353
|
+
onEnterScreen(screen, fn) {
|
|
354
|
+
const list = this._enterScreenHooks.get(screen) ?? [];
|
|
355
|
+
list.push(fn);
|
|
356
|
+
this._enterScreenHooks.set(screen, list);
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Detect screen transitions, run enter-screen hooks, and fire analytics.
|
|
360
|
+
* Called at the end of emitChange/pushOverlay/popOverlay.
|
|
361
|
+
*/
|
|
362
|
+
_detectTransition() {
|
|
363
|
+
const next = this.router.resolve(this.session);
|
|
364
|
+
const prev = this._lastScreen;
|
|
365
|
+
if (prev !== null && next !== prev) {
|
|
366
|
+
const hooks = this._enterScreenHooks.get(next);
|
|
367
|
+
if (hooks) for (const fn of hooks) fn();
|
|
368
|
+
analytics.wizardCapture(`screen ${next}`, {
|
|
369
|
+
from_screen: prev,
|
|
370
|
+
workflow: this.router.activeFlow,
|
|
371
|
+
...sessionProperties(this.session)
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
this._lastScreen = next;
|
|
375
|
+
}
|
|
376
|
+
pushStatus(message) {
|
|
377
|
+
const msgs = this.$statusMessages.get();
|
|
378
|
+
if (msgs.length > 0 && msgs[msgs.length - 1] === message) return;
|
|
379
|
+
this.$statusMessages.set([...msgs, message]);
|
|
380
|
+
this.emitChange();
|
|
381
|
+
}
|
|
382
|
+
setTasks(tasks) {
|
|
383
|
+
this.$tasks.set(tasks);
|
|
384
|
+
this.emitChange();
|
|
385
|
+
}
|
|
386
|
+
updateTask(index, done) {
|
|
387
|
+
const tasks = this.$tasks.get();
|
|
388
|
+
if (tasks[index]) {
|
|
389
|
+
const updated = [...tasks];
|
|
390
|
+
updated[index] = {
|
|
391
|
+
...updated[index],
|
|
392
|
+
done,
|
|
393
|
+
status: done ? "completed" : "pending"
|
|
394
|
+
};
|
|
395
|
+
this.$tasks.set(updated);
|
|
396
|
+
this.emitChange();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
setEventPlan(events) {
|
|
400
|
+
this.$eventPlan.set(events);
|
|
401
|
+
this.emitChange();
|
|
402
|
+
}
|
|
403
|
+
get learnCardBlockIdx() {
|
|
404
|
+
return this.$learnCardBlockIdx.get();
|
|
405
|
+
}
|
|
406
|
+
setLearnCardBlockIdx(idx) {
|
|
407
|
+
this.$learnCardBlockIdx.set(idx);
|
|
408
|
+
}
|
|
409
|
+
get learnCardComplete() {
|
|
410
|
+
return this.$learnCardComplete.get();
|
|
411
|
+
}
|
|
412
|
+
setLearnCardComplete() {
|
|
413
|
+
this.$learnCardComplete.set(true);
|
|
414
|
+
this.emitChange();
|
|
415
|
+
}
|
|
416
|
+
syncTodos(todos) {
|
|
417
|
+
const incoming = todos.map((t) => ({
|
|
418
|
+
label: t.content,
|
|
419
|
+
activeForm: t.activeForm,
|
|
420
|
+
status: t.status || "pending",
|
|
421
|
+
done: t.status === "completed"
|
|
422
|
+
}));
|
|
423
|
+
const incomingLabels = new Set(incoming.map((t) => t.label));
|
|
424
|
+
const retained = this.$tasks.get().filter((t) => t.done && !incomingLabels.has(t.label));
|
|
425
|
+
this.$tasks.set([...retained, ...incoming]);
|
|
426
|
+
this.emitChange();
|
|
427
|
+
}
|
|
428
|
+
subscribe(callback) {
|
|
429
|
+
return this.$version.listen(() => callback());
|
|
430
|
+
}
|
|
431
|
+
getSnapshot() {
|
|
432
|
+
return this.$version.get();
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/ui/tui/styles.ts
|
|
437
|
+
const Colors = {
|
|
438
|
+
primary: "cyan",
|
|
439
|
+
accent: "#DC9300",
|
|
440
|
+
titleColor: "#3D2800",
|
|
441
|
+
success: "green",
|
|
442
|
+
error: "red",
|
|
443
|
+
muted: "gray"
|
|
444
|
+
};
|
|
445
|
+
const Icons = {
|
|
446
|
+
diamond: "◆",
|
|
447
|
+
diamondOpen: "◇",
|
|
448
|
+
check: "✔",
|
|
449
|
+
warning: "⚠",
|
|
450
|
+
squareFilled: "◼",
|
|
451
|
+
squareOpen: "◻",
|
|
452
|
+
triangleRight: "▶",
|
|
453
|
+
triangleSmallRight: "▸",
|
|
454
|
+
bullet: "•"
|
|
455
|
+
};
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region src/ui/tui/primitives/CardLayout.tsx
|
|
458
|
+
/**
|
|
459
|
+
* CardLayout — Aligns a single child within available space.
|
|
460
|
+
*/
|
|
461
|
+
const CardLayout = ({ hAlign = "flex-start", vAlign = "flex-start", children }) => {
|
|
462
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
463
|
+
flexDirection: "column",
|
|
464
|
+
flexGrow: 1,
|
|
465
|
+
justifyContent: vAlign,
|
|
466
|
+
alignItems: hAlign,
|
|
467
|
+
children
|
|
468
|
+
});
|
|
469
|
+
};
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/ui/tui/primitives/SplitView.tsx
|
|
472
|
+
/**
|
|
473
|
+
* SplitView — Two-pane horizontal layout: 50/50.
|
|
474
|
+
*/
|
|
475
|
+
const SplitView = ({ left, right, gap = 2 }) => {
|
|
476
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
477
|
+
flexDirection: "row",
|
|
478
|
+
flexGrow: 1,
|
|
479
|
+
flexShrink: 1,
|
|
480
|
+
gap,
|
|
481
|
+
children: [/* @__PURE__ */ jsx(Box, {
|
|
482
|
+
width: "50%",
|
|
483
|
+
flexDirection: "column",
|
|
484
|
+
overflow: "hidden",
|
|
485
|
+
children: left
|
|
486
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
487
|
+
width: "50%",
|
|
488
|
+
flexDirection: "column",
|
|
489
|
+
overflow: "hidden",
|
|
490
|
+
children: right
|
|
491
|
+
})]
|
|
492
|
+
});
|
|
493
|
+
};
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/ui/tui/primitives/LoadingBox.tsx
|
|
496
|
+
/**
|
|
497
|
+
* LoadingBox — Spinner with message.
|
|
498
|
+
*/
|
|
499
|
+
const LoadingBox = ({ message }) => {
|
|
500
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
501
|
+
gap: 1,
|
|
502
|
+
children: [/* @__PURE__ */ jsx(Spinner, {}), /* @__PURE__ */ jsx(Text, { children: message })]
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/ui/tui/primitives/ProgressList.tsx
|
|
507
|
+
/**
|
|
508
|
+
* ProgressList — Reusable task checklist with status icons.
|
|
509
|
+
* Extracted from StatusTab logic.
|
|
510
|
+
*/
|
|
511
|
+
const ProgressList = ({ items, title }) => {
|
|
512
|
+
const completed = items.filter((t) => t.status === "completed").length;
|
|
513
|
+
const total = items.length;
|
|
514
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
515
|
+
flexDirection: "column",
|
|
516
|
+
children: [
|
|
517
|
+
title && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, {
|
|
518
|
+
bold: true,
|
|
519
|
+
children: title
|
|
520
|
+
}), /* @__PURE__ */ jsx(Text, { children: " " })] }),
|
|
521
|
+
items.length === 0 && /* @__PURE__ */ jsx(LoadingBox, { message: "Analyzing project..." }),
|
|
522
|
+
items.map((item, i) => {
|
|
523
|
+
const icon = item.status === "completed" ? Icons.squareFilled : item.status === "in_progress" ? Icons.triangleRight : Icons.squareOpen;
|
|
524
|
+
const color = item.status === "completed" ? Colors.success : item.status === "in_progress" ? Colors.primary : Colors.muted;
|
|
525
|
+
const label = item.status === "in_progress" && item.activeForm ? item.activeForm : item.label;
|
|
526
|
+
return /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
|
|
527
|
+
color,
|
|
528
|
+
children: icon
|
|
529
|
+
}), /* @__PURE__ */ jsxs(Text, {
|
|
530
|
+
dimColor: item.status === "pending",
|
|
531
|
+
children: [" ", label]
|
|
532
|
+
})] }, i);
|
|
533
|
+
}),
|
|
534
|
+
total > 0 && /* @__PURE__ */ jsxs(Box, {
|
|
535
|
+
marginTop: 1,
|
|
536
|
+
gap: 1,
|
|
537
|
+
children: [/* @__PURE__ */ jsx(Spinner, {}), /* @__PURE__ */ jsx(Text, {
|
|
538
|
+
dimColor: true,
|
|
539
|
+
children: completed < total ? `Progress: ${completed}/${total} completed` : "Cleaning up..."
|
|
540
|
+
})]
|
|
541
|
+
})
|
|
542
|
+
]
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
//#endregion
|
|
546
|
+
//#region src/ui/tui/primitives/PromptLabel.tsx
|
|
547
|
+
/**
|
|
548
|
+
* PromptLabel — Compact inline label for input prompts.
|
|
549
|
+
*
|
|
550
|
+
* Renders: [!] message
|
|
551
|
+
* where [!] is black text on accent background.
|
|
552
|
+
*/
|
|
553
|
+
const PromptLabel = ({ message }) => {
|
|
554
|
+
return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, {
|
|
555
|
+
bold: true,
|
|
556
|
+
color: Colors.accent,
|
|
557
|
+
children: [" ", message]
|
|
558
|
+
}) });
|
|
559
|
+
};
|
|
560
|
+
//#endregion
|
|
561
|
+
//#region src/ui/tui/hooks/keyboard-hints-utils.ts
|
|
562
|
+
/** Default priorities by key type. */
|
|
563
|
+
const DEFAULT_PRIORITY = {
|
|
564
|
+
["upArrow"]: 0,
|
|
565
|
+
["downArrow"]: 0,
|
|
566
|
+
["leftArrow"]: 1,
|
|
567
|
+
["rightArrow"]: 1,
|
|
568
|
+
["space"]: 10,
|
|
569
|
+
["escape"]: 20,
|
|
570
|
+
["return"]: 21
|
|
571
|
+
};
|
|
572
|
+
/** Get the default display priority for a key match. */
|
|
573
|
+
function getDefaultPriority(match) {
|
|
574
|
+
return DEFAULT_PRIORITY[Array.isArray(match) ? match[0] : match] ?? 15;
|
|
575
|
+
}
|
|
576
|
+
/** Check if a KeyMatchOrChar matches the given input string and key flags. */
|
|
577
|
+
function matchesKey(m, input, key) {
|
|
578
|
+
switch (m) {
|
|
579
|
+
case "upArrow": return !!key.upArrow;
|
|
580
|
+
case "downArrow": return !!key.downArrow;
|
|
581
|
+
case "leftArrow": return !!key.leftArrow;
|
|
582
|
+
case "rightArrow": return !!key.rightArrow;
|
|
583
|
+
case "return": return !!key.return;
|
|
584
|
+
case "escape": return !!key.escape;
|
|
585
|
+
case "space": return input === " ";
|
|
586
|
+
default: return input === m;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
/** Serialize hints for comparison. */
|
|
590
|
+
function hintsKey(hints) {
|
|
591
|
+
return hints.map((h) => `${h.label}:${h.action}`).join("|");
|
|
592
|
+
}
|
|
593
|
+
/** Deduplicate hints by label+action and sort by priority. */
|
|
594
|
+
function deduplicateAndSort(hints) {
|
|
595
|
+
const seen = /* @__PURE__ */ new Set();
|
|
596
|
+
const deduped = [];
|
|
597
|
+
for (const hint of hints) {
|
|
598
|
+
const k = `${hint.label}:${hint.action}`;
|
|
599
|
+
if (!seen.has(k)) {
|
|
600
|
+
seen.add(k);
|
|
601
|
+
deduped.push(hint);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
deduped.sort((a, b) => a.priority - b.priority);
|
|
605
|
+
return deduped;
|
|
606
|
+
}
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region src/ui/tui/hooks/useKeyboardHints.tsx
|
|
609
|
+
/**
|
|
610
|
+
* KeyboardHintsProvider — Context for collecting and displaying keyboard hints.
|
|
611
|
+
*
|
|
612
|
+
* Input components register their hints via useKeyBindings. The provider
|
|
613
|
+
* flattens, deduplicates, and sorts them. It auto-dismisses 3s after the
|
|
614
|
+
* first keypress and resets when the hint set changes (screen navigation).
|
|
615
|
+
*/
|
|
616
|
+
const KeyboardHintsContext = createContext({
|
|
617
|
+
register: () => void 0,
|
|
618
|
+
unregister: () => void 0,
|
|
619
|
+
hints: [],
|
|
620
|
+
visible: false
|
|
621
|
+
});
|
|
622
|
+
const useKeyboardHintsContext = () => useContext(KeyboardHintsContext);
|
|
623
|
+
const DISMISS_DELAY = 3e3;
|
|
624
|
+
const KeyboardHintsProvider = ({ children }) => {
|
|
625
|
+
const registrationsRef = useRef(/* @__PURE__ */ new Map());
|
|
626
|
+
const [hints, setHints] = useState([]);
|
|
627
|
+
const [visible, setVisible] = useState(true);
|
|
628
|
+
const timerRef = useRef(null);
|
|
629
|
+
const prevHintsKeyRef = useRef("");
|
|
630
|
+
const recompute = useCallback(() => {
|
|
631
|
+
const all = [];
|
|
632
|
+
for (const h of registrationsRef.current.values()) all.push(...h);
|
|
633
|
+
const deduped = deduplicateAndSort(all);
|
|
634
|
+
const newKey = hintsKey(deduped);
|
|
635
|
+
if (newKey !== prevHintsKeyRef.current) {
|
|
636
|
+
prevHintsKeyRef.current = newKey;
|
|
637
|
+
setHints(deduped);
|
|
638
|
+
if (newKey.length > 0) {
|
|
639
|
+
setVisible(true);
|
|
640
|
+
if (timerRef.current) {
|
|
641
|
+
clearTimeout(timerRef.current);
|
|
642
|
+
timerRef.current = null;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}, []);
|
|
647
|
+
const register = useCallback((id, h) => {
|
|
648
|
+
registrationsRef.current.set(id, h);
|
|
649
|
+
recompute();
|
|
650
|
+
}, [recompute]);
|
|
651
|
+
const unregister = useCallback((id) => {
|
|
652
|
+
registrationsRef.current.delete(id);
|
|
653
|
+
recompute();
|
|
654
|
+
}, [recompute]);
|
|
655
|
+
useInput(() => {
|
|
656
|
+
if (!visible) return;
|
|
657
|
+
if (timerRef.current) return;
|
|
658
|
+
timerRef.current = setTimeout(() => {
|
|
659
|
+
setVisible(false);
|
|
660
|
+
timerRef.current = null;
|
|
661
|
+
}, DISMISS_DELAY);
|
|
662
|
+
});
|
|
663
|
+
useEffect(() => {
|
|
664
|
+
return () => {
|
|
665
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
666
|
+
};
|
|
667
|
+
}, []);
|
|
668
|
+
return /* @__PURE__ */ jsx(KeyboardHintsContext.Provider, {
|
|
669
|
+
value: {
|
|
670
|
+
register,
|
|
671
|
+
unregister,
|
|
672
|
+
hints,
|
|
673
|
+
visible
|
|
674
|
+
},
|
|
675
|
+
children
|
|
676
|
+
});
|
|
677
|
+
};
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/ui/tui/hooks/useKeyBindings.ts
|
|
680
|
+
/**
|
|
681
|
+
* useKeyBindings — Declarative keyboard input + automatic hint registration.
|
|
682
|
+
*
|
|
683
|
+
* Replaces raw `useInput` in input components. Define bindings as data;
|
|
684
|
+
* the hook wires up the Ink input handler AND registers hints in the
|
|
685
|
+
* KeyboardHintsProvider. One source of truth for keys and their labels.
|
|
686
|
+
*/
|
|
687
|
+
/**
|
|
688
|
+
* Declarative key bindings hook. Replaces `useInput` in input components.
|
|
689
|
+
* Registers hints automatically with the KeyboardHintsProvider.
|
|
690
|
+
*
|
|
691
|
+
* @param id Unique identifier for this component's hints registration
|
|
692
|
+
* @param bindings Array of key binding definitions
|
|
693
|
+
*/
|
|
694
|
+
function useKeyBindings(id, bindings) {
|
|
695
|
+
const ctx = useKeyboardHintsContext();
|
|
696
|
+
const hintsRef = useRef("");
|
|
697
|
+
const hints = bindings.map((b) => ({
|
|
698
|
+
label: b.label,
|
|
699
|
+
action: b.action,
|
|
700
|
+
priority: b.priority ?? getDefaultPriority(b.match)
|
|
701
|
+
}));
|
|
702
|
+
const serialized = hints.map((h) => `${h.label}:${h.action}:${h.priority}`).join("|");
|
|
703
|
+
useEffect(() => {
|
|
704
|
+
if (serialized !== hintsRef.current) {
|
|
705
|
+
hintsRef.current = serialized;
|
|
706
|
+
ctx.register(id, hints);
|
|
707
|
+
}
|
|
708
|
+
return () => ctx.unregister(id);
|
|
709
|
+
}, [id, serialized]);
|
|
710
|
+
useInput((input, key) => {
|
|
711
|
+
for (const binding of bindings) if ((Array.isArray(binding.match) ? binding.match : [binding.match]).some((m) => matchesKey(m, input, key))) {
|
|
712
|
+
binding.handler(input, key);
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
//#endregion
|
|
718
|
+
//#region src/ui/tui/primitives/PickerMenu.tsx
|
|
719
|
+
/**
|
|
720
|
+
* PickerMenu — Single and multi select.
|
|
721
|
+
* Single mode: custom renderer with small triangle indicator.
|
|
722
|
+
* Multi mode: checkbox glyphs with space to toggle.
|
|
723
|
+
*
|
|
724
|
+
* Key bindings are declared via useKeyBindings, which auto-registers
|
|
725
|
+
* hints in the KeyboardHintsBar.
|
|
726
|
+
*/
|
|
727
|
+
const PickerMenu = ({ message, options, mode = "single", centered = false, columns = 1, onSelect }) => {
|
|
728
|
+
if (mode === "multi") return /* @__PURE__ */ jsx(MultiPickerMenu, {
|
|
729
|
+
message,
|
|
730
|
+
options,
|
|
731
|
+
centered,
|
|
732
|
+
columns,
|
|
733
|
+
onSelect
|
|
734
|
+
});
|
|
735
|
+
return /* @__PURE__ */ jsx(SinglePickerMenu, {
|
|
736
|
+
message,
|
|
737
|
+
options,
|
|
738
|
+
centered,
|
|
739
|
+
columns,
|
|
740
|
+
onSelect
|
|
741
|
+
});
|
|
742
|
+
};
|
|
743
|
+
/** Custom single-select with triangle indicator and accent highlight. */
|
|
744
|
+
const SinglePickerMenu = ({ message, options, centered = false, columns = 1, onSelect }) => {
|
|
745
|
+
const [focused, setFocused] = useState(0);
|
|
746
|
+
const rows = Math.ceil(options.length / columns);
|
|
747
|
+
const bindings = [{
|
|
748
|
+
match: ["upArrow", "downArrow"],
|
|
749
|
+
label: "↑↓",
|
|
750
|
+
action: "navigate",
|
|
751
|
+
handler: (_input, key) => {
|
|
752
|
+
const col = Math.floor(focused / rows);
|
|
753
|
+
const row = focused % rows;
|
|
754
|
+
if (key.upArrow) if (row > 0) setFocused(col * rows + row - 1);
|
|
755
|
+
else setFocused(Math.min(col * rows + rows - 1, options.length - 1));
|
|
756
|
+
if (key.downArrow) {
|
|
757
|
+
const next = col * rows + row + 1;
|
|
758
|
+
if (next < options.length && row + 1 < rows) setFocused(next);
|
|
759
|
+
else setFocused(col * rows);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}, {
|
|
763
|
+
match: "return",
|
|
764
|
+
label: "enter",
|
|
765
|
+
action: "select",
|
|
766
|
+
handler: () => {
|
|
767
|
+
const selected = options[focused];
|
|
768
|
+
if (selected) onSelect(selected.value);
|
|
769
|
+
}
|
|
770
|
+
}];
|
|
771
|
+
if (columns > 1) bindings.splice(1, 0, {
|
|
772
|
+
match: ["leftArrow", "rightArrow"],
|
|
773
|
+
label: "←→",
|
|
774
|
+
action: "navigate",
|
|
775
|
+
handler: (_input, key) => {
|
|
776
|
+
const col = Math.floor(focused / rows);
|
|
777
|
+
const row = focused % rows;
|
|
778
|
+
if (key.leftArrow) {
|
|
779
|
+
const prevCol = col > 0 ? col - 1 : columns - 1;
|
|
780
|
+
setFocused(Math.min(prevCol * rows + row, options.length - 1));
|
|
781
|
+
}
|
|
782
|
+
if (key.rightArrow) {
|
|
783
|
+
const nextCol = col < columns - 1 ? col + 1 : 0;
|
|
784
|
+
setFocused(Math.min(nextCol * rows + row, options.length - 1));
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
useKeyBindings("single-picker", bindings);
|
|
789
|
+
const columnArrays = [];
|
|
790
|
+
for (let c = 0; c < columns; c++) columnArrays.push(options.slice(c * rows, c * rows + rows));
|
|
791
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
792
|
+
flexDirection: "column",
|
|
793
|
+
alignItems: centered ? "center" : void 0,
|
|
794
|
+
children: [/* @__PURE__ */ jsx(PromptLabel, { message }), /* @__PURE__ */ jsx(Box, {
|
|
795
|
+
flexDirection: "row",
|
|
796
|
+
gap: 4,
|
|
797
|
+
children: columnArrays.map((colOpts, colIdx) => /* @__PURE__ */ jsx(Box, {
|
|
798
|
+
flexDirection: "column",
|
|
799
|
+
children: colOpts.map((opt, rowIdx) => {
|
|
800
|
+
const flatIdx = colIdx * rows + rowIdx;
|
|
801
|
+
const isFocused = flatIdx === focused;
|
|
802
|
+
const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
|
|
803
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
804
|
+
gap: 1,
|
|
805
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
806
|
+
color: isFocused ? Colors.accent : void 0,
|
|
807
|
+
dimColor: !isFocused,
|
|
808
|
+
children: isFocused ? Icons.triangleSmallRight : " "
|
|
809
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
810
|
+
color: isFocused ? Colors.accent : void 0,
|
|
811
|
+
bold: isFocused,
|
|
812
|
+
dimColor: !isFocused,
|
|
813
|
+
children: label
|
|
814
|
+
})]
|
|
815
|
+
}, flatIdx);
|
|
816
|
+
})
|
|
817
|
+
}, colIdx))
|
|
818
|
+
})]
|
|
819
|
+
});
|
|
820
|
+
};
|
|
821
|
+
/** Custom multi-select with checkbox glyphs and accent highlight. */
|
|
822
|
+
const MultiPickerMenu = ({ message, options, centered = false, columns = 1, onSelect }) => {
|
|
823
|
+
const [focused, setFocused] = useState(0);
|
|
824
|
+
const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
|
|
825
|
+
const rows = Math.ceil(options.length / columns);
|
|
826
|
+
const bindings = [
|
|
827
|
+
{
|
|
828
|
+
match: ["upArrow", "downArrow"],
|
|
829
|
+
label: "↑↓",
|
|
830
|
+
action: "navigate",
|
|
831
|
+
handler: (_input, key) => {
|
|
832
|
+
const col = Math.floor(focused / rows);
|
|
833
|
+
const row = focused % rows;
|
|
834
|
+
if (key.upArrow) if (row > 0) setFocused(col * rows + row - 1);
|
|
835
|
+
else setFocused(Math.min(col * rows + rows - 1, options.length - 1));
|
|
836
|
+
if (key.downArrow) {
|
|
837
|
+
const next = col * rows + row + 1;
|
|
838
|
+
if (next < options.length && row + 1 < rows) setFocused(next);
|
|
839
|
+
else setFocused(col * rows);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
match: "space",
|
|
845
|
+
label: "space",
|
|
846
|
+
action: "toggle",
|
|
847
|
+
handler: () => {
|
|
848
|
+
setSelected((prev) => {
|
|
849
|
+
const next = new Set(prev);
|
|
850
|
+
if (next.has(focused)) next.delete(focused);
|
|
851
|
+
else next.add(focused);
|
|
852
|
+
return next;
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
match: "return",
|
|
858
|
+
label: "enter",
|
|
859
|
+
action: "confirm",
|
|
860
|
+
handler: () => {
|
|
861
|
+
if (selected.size === 0) {
|
|
862
|
+
const hovered = options[focused];
|
|
863
|
+
if (hovered) onSelect(hovered.value);
|
|
864
|
+
} else onSelect([...selected].sort().map((i) => options[i].value));
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
];
|
|
868
|
+
if (columns > 1) bindings.splice(1, 0, {
|
|
869
|
+
match: ["leftArrow", "rightArrow"],
|
|
870
|
+
label: "←→",
|
|
871
|
+
action: "navigate",
|
|
872
|
+
handler: (_input, key) => {
|
|
873
|
+
const col = Math.floor(focused / rows);
|
|
874
|
+
const row = focused % rows;
|
|
875
|
+
if (key.leftArrow) {
|
|
876
|
+
const prevCol = col > 0 ? col - 1 : columns - 1;
|
|
877
|
+
setFocused(Math.min(prevCol * rows + row, options.length - 1));
|
|
878
|
+
}
|
|
879
|
+
if (key.rightArrow) {
|
|
880
|
+
const nextCol = col < columns - 1 ? col + 1 : 0;
|
|
881
|
+
setFocused(Math.min(nextCol * rows + row, options.length - 1));
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
useKeyBindings("multi-picker", bindings);
|
|
886
|
+
const columnArrays = [];
|
|
887
|
+
for (let c = 0; c < columns; c++) columnArrays.push(options.slice(c * rows, c * rows + rows));
|
|
888
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
889
|
+
flexDirection: "column",
|
|
890
|
+
alignItems: centered ? "center" : void 0,
|
|
891
|
+
children: [/* @__PURE__ */ jsx(PromptLabel, { message }), /* @__PURE__ */ jsx(Box, {
|
|
892
|
+
flexDirection: "row",
|
|
893
|
+
gap: 4,
|
|
894
|
+
marginLeft: centered ? 0 : 2,
|
|
895
|
+
marginTop: 1,
|
|
896
|
+
children: columnArrays.map((colOpts, colIdx) => /* @__PURE__ */ jsx(Box, {
|
|
897
|
+
flexDirection: "column",
|
|
898
|
+
children: colOpts.map((opt, rowIdx) => {
|
|
899
|
+
const flatIdx = colIdx * rows + rowIdx;
|
|
900
|
+
const isFocused = flatIdx === focused;
|
|
901
|
+
const isSelected = selected.has(flatIdx);
|
|
902
|
+
const label = opt.hint ? `${opt.label} (${opt.hint})` : opt.label;
|
|
903
|
+
const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
|
|
904
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
905
|
+
gap: 1,
|
|
906
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
907
|
+
color: isSelected ? "white" : Colors.muted,
|
|
908
|
+
dimColor: !isFocused && !isSelected,
|
|
909
|
+
children: checkbox
|
|
910
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
911
|
+
color: isFocused ? Colors.accent : void 0,
|
|
912
|
+
bold: isFocused,
|
|
913
|
+
dimColor: !isFocused,
|
|
914
|
+
children: label
|
|
915
|
+
})]
|
|
916
|
+
}, flatIdx);
|
|
917
|
+
})
|
|
918
|
+
}, colIdx))
|
|
919
|
+
})]
|
|
920
|
+
});
|
|
921
|
+
};
|
|
922
|
+
//#endregion
|
|
923
|
+
//#region src/ui/tui/hooks/useStdoutDimensions.ts
|
|
924
|
+
/**
|
|
925
|
+
* useStdoutDimensions — Returns [columns, rows] and re-renders on terminal resize.
|
|
926
|
+
*
|
|
927
|
+
* Ink's useStdout() does not subscribe to resize events, so layout only updates
|
|
928
|
+
* when something else causes a re-render. This hook listens to the stream's
|
|
929
|
+
* 'resize' event (Node TTY) and updates state so the component re-renders
|
|
930
|
+
* with the new dimensions.
|
|
931
|
+
*/
|
|
932
|
+
function useStdoutDimensions() {
|
|
933
|
+
const { stdout } = useStdout();
|
|
934
|
+
const [size, setSize] = useState(() => [stdout.columns || 80, stdout.rows || 24]);
|
|
935
|
+
useEffect(() => {
|
|
936
|
+
setSize([stdout.columns || 80, stdout.rows || 24]);
|
|
937
|
+
const stream = stdout;
|
|
938
|
+
if (typeof stream.on !== "function") return;
|
|
939
|
+
const onResize = () => {
|
|
940
|
+
const c = stdout.columns || 80;
|
|
941
|
+
const r = stdout.rows || 24;
|
|
942
|
+
if (c > 0 && r > 0) setSize([c, r]);
|
|
943
|
+
};
|
|
944
|
+
stream.on("resize", onResize);
|
|
945
|
+
return () => {
|
|
946
|
+
stream.off?.("resize", onResize);
|
|
947
|
+
};
|
|
948
|
+
}, [stdout]);
|
|
949
|
+
return size;
|
|
950
|
+
}
|
|
951
|
+
//#endregion
|
|
952
|
+
//#region src/ui/tui/primitives/GroupedPickerMenu.tsx
|
|
953
|
+
/**
|
|
954
|
+
* GroupedPickerMenu — Multi-select with category headers.
|
|
955
|
+
*
|
|
956
|
+
* Renders groups of options with bold category labels.
|
|
957
|
+
* Arrow keys navigate selectable items (headers are skipped),
|
|
958
|
+
* space toggles, "a" toggles all, enter submits.
|
|
959
|
+
*
|
|
960
|
+
* When content exceeds available terminal height, the list scrolls
|
|
961
|
+
* to keep the focused item visible with up/down indicators.
|
|
962
|
+
*
|
|
963
|
+
* Key bindings are declared via useKeyBindings, which auto-registers
|
|
964
|
+
* hints in the KeyboardHintsBar.
|
|
965
|
+
*/
|
|
966
|
+
/** Truncate text with "\u2026" if it exceeds maxWidth. */
|
|
967
|
+
function truncateWithEllipsis(text, maxWidth) {
|
|
968
|
+
if (text.length <= maxWidth) return text;
|
|
969
|
+
return text.slice(0, maxWidth - 1) + "…";
|
|
970
|
+
}
|
|
971
|
+
/** Rows consumed by chrome outside this component (title bar, screen padding, etc.) */
|
|
972
|
+
const CHROME_OVERHEAD = 10;
|
|
973
|
+
/** Rows used by the prompt label + marginTop before content (hint text moved to KeyboardHintsBar). */
|
|
974
|
+
const MENU_CHROME = 2;
|
|
975
|
+
/** Count the visual rows occupied by rows[start..end), accounting for header margins. */
|
|
976
|
+
function countVisualRows(rows, start, end) {
|
|
977
|
+
let count = 0;
|
|
978
|
+
for (let i = start; i < end && i < rows.length; i++) {
|
|
979
|
+
if (rows[i].kind === "header" && i > start) count += 1;
|
|
980
|
+
count += 1;
|
|
981
|
+
}
|
|
982
|
+
return count;
|
|
983
|
+
}
|
|
984
|
+
/** From scrollOffset, find how many flat rows fit in the visual budget. */
|
|
985
|
+
function computeVisibleEnd(rows, scrollOffset, budget) {
|
|
986
|
+
let visualCount = 0;
|
|
987
|
+
let i = scrollOffset;
|
|
988
|
+
while (i < rows.length) {
|
|
989
|
+
const cost = rows[i].kind === "header" && i > scrollOffset ? 2 : 1;
|
|
990
|
+
if (visualCount + cost > budget) break;
|
|
991
|
+
visualCount += cost;
|
|
992
|
+
i++;
|
|
993
|
+
}
|
|
994
|
+
return i;
|
|
995
|
+
}
|
|
996
|
+
/** Adjust scroll offset to keep focusedRowIdx visible within the viewport. */
|
|
997
|
+
function adjustScrollOffset(currentOffset, focusedRowIdx, rows, viewportBudget) {
|
|
998
|
+
const visibleEnd = computeVisibleEnd(rows, currentOffset, viewportBudget);
|
|
999
|
+
if (focusedRowIdx >= currentOffset && focusedRowIdx < visibleEnd) return currentOffset;
|
|
1000
|
+
if (focusedRowIdx < currentOffset) {
|
|
1001
|
+
let newOffset = focusedRowIdx;
|
|
1002
|
+
if (newOffset > 0 && rows[newOffset - 1]?.kind === "header") newOffset--;
|
|
1003
|
+
return Math.max(0, newOffset);
|
|
1004
|
+
}
|
|
1005
|
+
let newOffset = currentOffset + 1;
|
|
1006
|
+
while (newOffset < rows.length) {
|
|
1007
|
+
if (focusedRowIdx < computeVisibleEnd(rows, newOffset, viewportBudget)) break;
|
|
1008
|
+
newOffset++;
|
|
1009
|
+
}
|
|
1010
|
+
return Math.min(newOffset, Math.max(0, rows.length - 1));
|
|
1011
|
+
}
|
|
1012
|
+
const GroupedPickerMenu = ({ message, groups, initialSelected, onSelect }) => {
|
|
1013
|
+
const [termCols, termRows] = useStdoutDimensions();
|
|
1014
|
+
const labelWidth = Math.max(10, Math.min(termCols, 120) - 8);
|
|
1015
|
+
const rows = useMemo(() => {
|
|
1016
|
+
const result = [];
|
|
1017
|
+
for (const [groupName, options] of Object.entries(groups)) {
|
|
1018
|
+
result.push({
|
|
1019
|
+
kind: "header",
|
|
1020
|
+
label: groupName
|
|
1021
|
+
});
|
|
1022
|
+
for (const opt of options) result.push({
|
|
1023
|
+
kind: "option",
|
|
1024
|
+
...opt
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
return result;
|
|
1028
|
+
}, [groups]);
|
|
1029
|
+
const selectableIndices = useMemo(() => rows.map((r, i) => r.kind === "option" ? i : -1).filter((i) => i >= 0), [rows]);
|
|
1030
|
+
const allValues = useMemo(() => rows.filter((r) => r.kind === "option").map((r) => r.value), [rows]);
|
|
1031
|
+
const [focusedSelectable, setFocusedSelectable] = useState(0);
|
|
1032
|
+
const [selected, setSelected] = useState(() => new Set(initialSelected ?? allValues));
|
|
1033
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
1034
|
+
const focusedRowIdx = selectableIndices[focusedSelectable] ?? 0;
|
|
1035
|
+
const viewportBudget = Math.max(5, termRows - CHROME_OVERHEAD - MENU_CHROME);
|
|
1036
|
+
const needsScroll = countVisualRows(rows, 0, rows.length) > viewportBudget;
|
|
1037
|
+
const effectiveBudget = needsScroll ? viewportBudget - 2 : viewportBudget;
|
|
1038
|
+
useKeyBindings("grouped-picker", [
|
|
1039
|
+
{
|
|
1040
|
+
match: ["upArrow", "downArrow"],
|
|
1041
|
+
label: "↑↓",
|
|
1042
|
+
action: "navigate",
|
|
1043
|
+
handler: (_input, key) => {
|
|
1044
|
+
let newFocused = focusedSelectable;
|
|
1045
|
+
if (key.upArrow) newFocused = focusedSelectable > 0 ? focusedSelectable - 1 : selectableIndices.length - 1;
|
|
1046
|
+
if (key.downArrow) newFocused = focusedSelectable < selectableIndices.length - 1 ? focusedSelectable + 1 : 0;
|
|
1047
|
+
if (newFocused !== focusedSelectable) {
|
|
1048
|
+
setFocusedSelectable(newFocused);
|
|
1049
|
+
if (needsScroll) {
|
|
1050
|
+
const newFocusedRowIdx = selectableIndices[newFocused] ?? 0;
|
|
1051
|
+
setScrollOffset((prev) => adjustScrollOffset(prev, newFocusedRowIdx, rows, effectiveBudget));
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
match: "space",
|
|
1058
|
+
label: "space",
|
|
1059
|
+
action: "toggle",
|
|
1060
|
+
handler: () => {
|
|
1061
|
+
const row = rows[selectableIndices[focusedSelectable] ?? 0];
|
|
1062
|
+
if (row?.kind === "option") setSelected((prev) => {
|
|
1063
|
+
const next = new Set(prev);
|
|
1064
|
+
if (next.has(row.value)) next.delete(row.value);
|
|
1065
|
+
else next.add(row.value);
|
|
1066
|
+
return next;
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
match: "a",
|
|
1072
|
+
label: "a",
|
|
1073
|
+
action: "toggle all",
|
|
1074
|
+
priority: 11,
|
|
1075
|
+
handler: () => {
|
|
1076
|
+
setSelected((prev) => {
|
|
1077
|
+
if (prev.size === allValues.length) return /* @__PURE__ */ new Set();
|
|
1078
|
+
return new Set(allValues);
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
match: "return",
|
|
1084
|
+
label: "enter",
|
|
1085
|
+
action: "confirm",
|
|
1086
|
+
handler: () => {
|
|
1087
|
+
onSelect([...selected]);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
]);
|
|
1091
|
+
const visibleStart = needsScroll ? scrollOffset : 0;
|
|
1092
|
+
const visibleEnd = needsScroll ? computeVisibleEnd(rows, visibleStart, effectiveBudget) : rows.length;
|
|
1093
|
+
const visibleRows = rows.slice(visibleStart, visibleEnd);
|
|
1094
|
+
const hiddenAbove = needsScroll ? selectableIndices.filter((s) => s < visibleStart).length : 0;
|
|
1095
|
+
const hiddenBelow = needsScroll ? selectableIndices.filter((s) => s >= visibleEnd).length : 0;
|
|
1096
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1097
|
+
flexDirection: "column",
|
|
1098
|
+
children: [/* @__PURE__ */ jsx(PromptLabel, { message }), /* @__PURE__ */ jsxs(Box, {
|
|
1099
|
+
flexDirection: "column",
|
|
1100
|
+
marginTop: 1,
|
|
1101
|
+
marginLeft: 2,
|
|
1102
|
+
children: [
|
|
1103
|
+
needsScroll && /* @__PURE__ */ jsx(Text, {
|
|
1104
|
+
dimColor: true,
|
|
1105
|
+
children: hiddenAbove > 0 ? `\u2191 ${hiddenAbove} more` : " "
|
|
1106
|
+
}),
|
|
1107
|
+
visibleRows.map((row, relIdx) => {
|
|
1108
|
+
const absIdx = visibleStart + relIdx;
|
|
1109
|
+
if (row.kind === "header") return /* @__PURE__ */ jsx(Box, {
|
|
1110
|
+
marginTop: relIdx > 0 && absIdx > 0 ? 1 : 0,
|
|
1111
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1112
|
+
bold: true,
|
|
1113
|
+
dimColor: true,
|
|
1114
|
+
children: row.label
|
|
1115
|
+
})
|
|
1116
|
+
}, `h-${absIdx}`);
|
|
1117
|
+
const isFocused = focusedRowIdx === absIdx;
|
|
1118
|
+
const isSelected = selected.has(row.value);
|
|
1119
|
+
const checkbox = isSelected ? Icons.squareFilled : Icons.squareOpen;
|
|
1120
|
+
const label = truncateWithEllipsis(row.hint ? `${row.label} (${row.hint})` : row.label, labelWidth);
|
|
1121
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1122
|
+
gap: 1,
|
|
1123
|
+
marginLeft: 1,
|
|
1124
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
1125
|
+
color: isSelected ? "white" : Colors.muted,
|
|
1126
|
+
dimColor: !isFocused && !isSelected,
|
|
1127
|
+
children: checkbox
|
|
1128
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
1129
|
+
flexGrow: 1,
|
|
1130
|
+
flexShrink: 1,
|
|
1131
|
+
overflow: "hidden",
|
|
1132
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1133
|
+
color: isFocused ? Colors.accent : void 0,
|
|
1134
|
+
bold: isFocused,
|
|
1135
|
+
dimColor: !isFocused,
|
|
1136
|
+
wrap: "truncate",
|
|
1137
|
+
children: label
|
|
1138
|
+
})
|
|
1139
|
+
})]
|
|
1140
|
+
}, row.value);
|
|
1141
|
+
}),
|
|
1142
|
+
needsScroll && /* @__PURE__ */ jsx(Text, {
|
|
1143
|
+
dimColor: true,
|
|
1144
|
+
children: hiddenBelow > 0 ? `\u2193 ${hiddenBelow} more` : " "
|
|
1145
|
+
})
|
|
1146
|
+
]
|
|
1147
|
+
})]
|
|
1148
|
+
});
|
|
1149
|
+
};
|
|
1150
|
+
//#endregion
|
|
1151
|
+
//#region src/ui/tui/primitives/ConfirmationInput.tsx
|
|
1152
|
+
/**
|
|
1153
|
+
* ConfirmationInput — Continue/cancel prompt.
|
|
1154
|
+
* Enter confirms, escape cancels. Arrow keys toggle focus.
|
|
1155
|
+
*
|
|
1156
|
+
* Key bindings are declared via useKeyBindings, which auto-registers
|
|
1157
|
+
* hints in the KeyboardHintsBar.
|
|
1158
|
+
*/
|
|
1159
|
+
const ConfirmationInput = ({ message, onConfirm, onCancel, confirmLabel = "Continue", cancelLabel = "Cancel" }) => {
|
|
1160
|
+
const [focused, setFocused] = useState("continue");
|
|
1161
|
+
useKeyBindings("confirmation", [
|
|
1162
|
+
{
|
|
1163
|
+
match: ["leftArrow", "rightArrow"],
|
|
1164
|
+
label: "←→",
|
|
1165
|
+
action: "switch",
|
|
1166
|
+
handler: () => {
|
|
1167
|
+
setFocused((f) => f === "continue" ? "cancel" : "continue");
|
|
1168
|
+
}
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
match: "return",
|
|
1172
|
+
label: "enter",
|
|
1173
|
+
action: "confirm",
|
|
1174
|
+
handler: () => {
|
|
1175
|
+
if (focused === "continue") onConfirm();
|
|
1176
|
+
else onCancel();
|
|
1177
|
+
}
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
match: "escape",
|
|
1181
|
+
label: "esc",
|
|
1182
|
+
action: "cancel",
|
|
1183
|
+
handler: () => {
|
|
1184
|
+
onCancel();
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
]);
|
|
1188
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1189
|
+
flexDirection: "column",
|
|
1190
|
+
children: [/* @__PURE__ */ jsx(PromptLabel, { message }), /* @__PURE__ */ jsxs(Box, {
|
|
1191
|
+
gap: 2,
|
|
1192
|
+
marginTop: 1,
|
|
1193
|
+
marginLeft: 2,
|
|
1194
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
1195
|
+
bold: focused === "continue",
|
|
1196
|
+
color: focused === "continue" ? Colors.accent : Colors.muted,
|
|
1197
|
+
children: [
|
|
1198
|
+
focused === "continue" ? Icons.triangleSmallRight : " ",
|
|
1199
|
+
" ",
|
|
1200
|
+
confirmLabel
|
|
1201
|
+
]
|
|
1202
|
+
}), /* @__PURE__ */ jsxs(Text, {
|
|
1203
|
+
bold: focused === "cancel",
|
|
1204
|
+
color: focused === "cancel" ? Colors.accent : Colors.muted,
|
|
1205
|
+
children: [
|
|
1206
|
+
focused === "cancel" ? Icons.triangleSmallRight : " ",
|
|
1207
|
+
" ",
|
|
1208
|
+
cancelLabel
|
|
1209
|
+
]
|
|
1210
|
+
})]
|
|
1211
|
+
})]
|
|
1212
|
+
});
|
|
1213
|
+
};
|
|
1214
|
+
//#endregion
|
|
1215
|
+
//#region src/ui/tui/primitives/Divider.tsx
|
|
1216
|
+
const Divider = ({ dimColor = true, char = "─" }) => {
|
|
1217
|
+
const ref = useRef(null);
|
|
1218
|
+
const [width, setWidth] = useState(0);
|
|
1219
|
+
useEffect(() => {
|
|
1220
|
+
if (ref.current) {
|
|
1221
|
+
const { width: measured } = measureElement(ref.current);
|
|
1222
|
+
setWidth(measured);
|
|
1223
|
+
}
|
|
1224
|
+
}, []);
|
|
1225
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1226
|
+
ref,
|
|
1227
|
+
width: "100%",
|
|
1228
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1229
|
+
dimColor,
|
|
1230
|
+
children: width > 0 ? char.repeat(width) : ""
|
|
1231
|
+
})
|
|
1232
|
+
});
|
|
1233
|
+
};
|
|
1234
|
+
//#endregion
|
|
1235
|
+
//#region src/ui/tui/primitives/ModalOverlay.tsx
|
|
1236
|
+
const ModalOverlay = ({ borderColor, title, titleColor, width = 68, children, feedback, footer }) => {
|
|
1237
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1238
|
+
flexDirection: "column",
|
|
1239
|
+
flexGrow: 1,
|
|
1240
|
+
alignItems: "center",
|
|
1241
|
+
justifyContent: "center",
|
|
1242
|
+
children: /* @__PURE__ */ jsxs(Box, {
|
|
1243
|
+
flexDirection: "column",
|
|
1244
|
+
borderStyle: "round",
|
|
1245
|
+
borderColor,
|
|
1246
|
+
paddingX: 3,
|
|
1247
|
+
paddingY: 1,
|
|
1248
|
+
width,
|
|
1249
|
+
children: [
|
|
1250
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1251
|
+
justifyContent: "center",
|
|
1252
|
+
marginBottom: 1,
|
|
1253
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1254
|
+
color: titleColor ?? borderColor,
|
|
1255
|
+
bold: true,
|
|
1256
|
+
children: title
|
|
1257
|
+
})
|
|
1258
|
+
}),
|
|
1259
|
+
children,
|
|
1260
|
+
feedback && /* @__PURE__ */ jsx(Box, {
|
|
1261
|
+
marginTop: 1,
|
|
1262
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1263
|
+
color: "yellow",
|
|
1264
|
+
children: feedback
|
|
1265
|
+
})
|
|
1266
|
+
}),
|
|
1267
|
+
footer && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Box, {
|
|
1268
|
+
marginY: 1,
|
|
1269
|
+
children: /* @__PURE__ */ jsx(Divider, {})
|
|
1270
|
+
}), footer] })
|
|
1271
|
+
]
|
|
1272
|
+
})
|
|
1273
|
+
});
|
|
1274
|
+
};
|
|
1275
|
+
//#endregion
|
|
1276
|
+
//#region src/ui/tui/primitives/LogViewer.tsx
|
|
1277
|
+
/**
|
|
1278
|
+
* LogViewer — Real-time log tail, pinned to available terminal height.
|
|
1279
|
+
* Only renders the last N lines that fit on screen.
|
|
1280
|
+
*/
|
|
1281
|
+
/** Rows consumed by TitleBar + spacer + ScreenContainer padding + status bar + tab bar */
|
|
1282
|
+
const CHROME_ROWS = 8;
|
|
1283
|
+
const LogViewer = ({ filePath, height }) => {
|
|
1284
|
+
const [, rows] = useStdoutDimensions();
|
|
1285
|
+
const visibleLines = height ?? Math.max(5, rows - CHROME_ROWS);
|
|
1286
|
+
const [lines, setLines] = useState([]);
|
|
1287
|
+
useEffect(() => {
|
|
1288
|
+
const readTail = () => {
|
|
1289
|
+
try {
|
|
1290
|
+
setLines(fs$1.readFileSync(filePath, "utf-8").split("\n").slice(-visibleLines));
|
|
1291
|
+
} catch {
|
|
1292
|
+
setLines(["(No log file found)"]);
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
readTail();
|
|
1296
|
+
let watcher;
|
|
1297
|
+
try {
|
|
1298
|
+
watcher = fs$1.watch(filePath, () => {
|
|
1299
|
+
readTail();
|
|
1300
|
+
});
|
|
1301
|
+
} catch {
|
|
1302
|
+
const interval = setInterval(() => {
|
|
1303
|
+
try {
|
|
1304
|
+
fs$1.accessSync(filePath);
|
|
1305
|
+
readTail();
|
|
1306
|
+
clearInterval(interval);
|
|
1307
|
+
watcher = fs$1.watch(filePath, () => readTail());
|
|
1308
|
+
} catch {}
|
|
1309
|
+
}, 1e3);
|
|
1310
|
+
return () => clearInterval(interval);
|
|
1311
|
+
}
|
|
1312
|
+
return () => {
|
|
1313
|
+
watcher?.close();
|
|
1314
|
+
};
|
|
1315
|
+
}, [filePath, visibleLines]);
|
|
1316
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1317
|
+
flexDirection: "column",
|
|
1318
|
+
height: visibleLines,
|
|
1319
|
+
children: lines.map((line, i) => /* @__PURE__ */ jsx(Text, {
|
|
1320
|
+
dimColor: true,
|
|
1321
|
+
wrap: "truncate",
|
|
1322
|
+
children: line
|
|
1323
|
+
}, i))
|
|
1324
|
+
});
|
|
1325
|
+
};
|
|
1326
|
+
//#endregion
|
|
1327
|
+
//#region src/ui/tui/primitives/EventPlanViewer.tsx
|
|
1328
|
+
/**
|
|
1329
|
+
* EventPlanViewer — Renders a table of planned analytics events.
|
|
1330
|
+
*/
|
|
1331
|
+
const EventPlanViewer = ({ events }) => {
|
|
1332
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1333
|
+
flexDirection: "column",
|
|
1334
|
+
paddingX: 1,
|
|
1335
|
+
children: [
|
|
1336
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1337
|
+
bold: true,
|
|
1338
|
+
children: "Event plan"
|
|
1339
|
+
}),
|
|
1340
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1341
|
+
events.map((event) => /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
1342
|
+
bold: true,
|
|
1343
|
+
children: event.name
|
|
1344
|
+
}), /* @__PURE__ */ jsxs(Text, {
|
|
1345
|
+
dimColor: true,
|
|
1346
|
+
children: [" ", event.description]
|
|
1347
|
+
})] }, event.name))
|
|
1348
|
+
]
|
|
1349
|
+
});
|
|
1350
|
+
};
|
|
1351
|
+
//#endregion
|
|
1352
|
+
//#region src/ui/tui/components/TitleBar.tsx
|
|
1353
|
+
const FEEDBACK = "Feedback: wizard@posthog.com ";
|
|
1354
|
+
const FEEDBACK_SHORT = " wizard@posthog.com ";
|
|
1355
|
+
const TitleBar = ({ version, width }) => {
|
|
1356
|
+
const fullTitle = ` PostHog Wizard v${version}`;
|
|
1357
|
+
const needShort = width < fullTitle.length + 29;
|
|
1358
|
+
const feedback = needShort ? FEEDBACK_SHORT : FEEDBACK;
|
|
1359
|
+
const title = needShort && fullTitle.length + feedback.length > width ? ` Wizard v${version}` : fullTitle;
|
|
1360
|
+
const gap = Math.max(0, width - title.length - feedback.length);
|
|
1361
|
+
const padding = " ".repeat(gap);
|
|
1362
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1363
|
+
width,
|
|
1364
|
+
overflow: "hidden",
|
|
1365
|
+
children: /* @__PURE__ */ jsxs(Text, {
|
|
1366
|
+
backgroundColor: Colors.accent,
|
|
1367
|
+
color: Colors.titleColor,
|
|
1368
|
+
children: [
|
|
1369
|
+
title,
|
|
1370
|
+
padding,
|
|
1371
|
+
feedback
|
|
1372
|
+
]
|
|
1373
|
+
})
|
|
1374
|
+
});
|
|
1375
|
+
};
|
|
1376
|
+
//#endregion
|
|
1377
|
+
//#region src/ui/tui/primitives/DissolveTransition.tsx
|
|
1378
|
+
/**
|
|
1379
|
+
* DissolveTransition — Column-sweep inspired by TTE's Sweep effect.
|
|
1380
|
+
*
|
|
1381
|
+
* Uses a SequenceEaser (in_out_circ) to activate columns with eased pacing.
|
|
1382
|
+
* Each activated column cycles through shade characters (░▒▓█) independently.
|
|
1383
|
+
*
|
|
1384
|
+
* Out phase: columns sweep, building up shade chars until solid █ (covers old content).
|
|
1385
|
+
* In phase: columns sweep in reverse, dissolving █ back through shades to empty (reveals new content).
|
|
1386
|
+
*/
|
|
1387
|
+
/** Shade characters in build-up order (light → solid). */
|
|
1388
|
+
const SHADES = [
|
|
1389
|
+
"░",
|
|
1390
|
+
"▒",
|
|
1391
|
+
"▓",
|
|
1392
|
+
"█"
|
|
1393
|
+
];
|
|
1394
|
+
/** How many ticks each shade character displays before advancing. */
|
|
1395
|
+
const TICKS_PER_SHADE = 2;
|
|
1396
|
+
/** Total ticks a column needs to complete its shade cycle. */
|
|
1397
|
+
const SHADE_CYCLE_TICKS = SHADES.length * TICKS_PER_SHADE;
|
|
1398
|
+
function easeInOutCirc(t) {
|
|
1399
|
+
if (t < .5) return (1 - Math.sqrt(1 - 4 * t * t)) / 2;
|
|
1400
|
+
return (Math.sqrt(1 - (2 * t - 2) ** 2) + 1) / 2;
|
|
1401
|
+
}
|
|
1402
|
+
const DissolveTransition = ({ transitionKey, width, height, children, direction = "left", duration = 2 }) => {
|
|
1403
|
+
const [phase, setPhase] = useState("idle");
|
|
1404
|
+
const [tick, setTick] = useState(0);
|
|
1405
|
+
const [activeDir, setActiveDir] = useState(direction);
|
|
1406
|
+
const prevKey = useRef(transitionKey);
|
|
1407
|
+
const pendingChildren = useRef(children);
|
|
1408
|
+
const [displayChildren, setDisplayChildren] = useState(children);
|
|
1409
|
+
const columnActivationTick = useRef([]);
|
|
1410
|
+
useEffect(() => {
|
|
1411
|
+
if (transitionKey !== prevKey.current) {
|
|
1412
|
+
prevKey.current = transitionKey;
|
|
1413
|
+
pendingChildren.current = children;
|
|
1414
|
+
setActiveDir(direction);
|
|
1415
|
+
setPhase("out");
|
|
1416
|
+
setTick(0);
|
|
1417
|
+
columnActivationTick.current = new Array(width).fill(-1);
|
|
1418
|
+
} else if (phase !== "idle") {
|
|
1419
|
+
setPhase("idle");
|
|
1420
|
+
setDisplayChildren(children);
|
|
1421
|
+
} else setDisplayChildren(children);
|
|
1422
|
+
}, [
|
|
1423
|
+
transitionKey,
|
|
1424
|
+
children,
|
|
1425
|
+
width,
|
|
1426
|
+
height,
|
|
1427
|
+
phase,
|
|
1428
|
+
direction
|
|
1429
|
+
]);
|
|
1430
|
+
useEffect(() => {
|
|
1431
|
+
if (phase === "idle") return;
|
|
1432
|
+
const timer = setInterval(() => {
|
|
1433
|
+
setTick((prev) => prev + 1);
|
|
1434
|
+
}, duration);
|
|
1435
|
+
return () => clearInterval(timer);
|
|
1436
|
+
}, [phase, duration]);
|
|
1437
|
+
const easerSteps = width;
|
|
1438
|
+
const maxTicks = easerSteps + SHADE_CYCLE_TICKS;
|
|
1439
|
+
useEffect(() => {
|
|
1440
|
+
if (phase === "idle") return;
|
|
1441
|
+
if (tick >= maxTicks) if (phase === "out") {
|
|
1442
|
+
setDisplayChildren(pendingChildren.current);
|
|
1443
|
+
setPhase("in");
|
|
1444
|
+
setTick(0);
|
|
1445
|
+
columnActivationTick.current = new Array(width).fill(-1);
|
|
1446
|
+
} else setPhase("idle");
|
|
1447
|
+
}, [
|
|
1448
|
+
tick,
|
|
1449
|
+
phase,
|
|
1450
|
+
maxTicks,
|
|
1451
|
+
width
|
|
1452
|
+
]);
|
|
1453
|
+
if (phase === "idle") return /* @__PURE__ */ jsx(Fragment, { children: displayChildren });
|
|
1454
|
+
const easedValue = easeInOutCirc(Math.min(tick / easerSteps, 1));
|
|
1455
|
+
const activatedCount = Math.floor(easedValue * width);
|
|
1456
|
+
const columnOrder = [];
|
|
1457
|
+
if (activeDir === "left") for (let c = width - 1; c >= 0; c--) columnOrder.push(c);
|
|
1458
|
+
else for (let c = 0; c < width; c++) columnOrder.push(c);
|
|
1459
|
+
for (let i = 0; i < activatedCount && i < columnOrder.length; i++) {
|
|
1460
|
+
const col = columnOrder[i];
|
|
1461
|
+
if (columnActivationTick.current[col] === -1) columnActivationTick.current[col] = tick;
|
|
1462
|
+
}
|
|
1463
|
+
const rows = [];
|
|
1464
|
+
for (let r = 0; r < height; r++) {
|
|
1465
|
+
let row = "";
|
|
1466
|
+
for (let c = 0; c < width; c++) {
|
|
1467
|
+
const activatedAt = columnActivationTick.current[c];
|
|
1468
|
+
let char;
|
|
1469
|
+
if (activatedAt === -1) char = phase === "out" ? " " : "█";
|
|
1470
|
+
else {
|
|
1471
|
+
const age = tick - activatedAt;
|
|
1472
|
+
const shadeIndex = Math.min(Math.floor(age / TICKS_PER_SHADE), SHADES.length - 1);
|
|
1473
|
+
if (phase === "out") char = SHADES[shadeIndex];
|
|
1474
|
+
else if (shadeIndex >= SHADES.length - 1 && age >= SHADE_CYCLE_TICKS) char = " ";
|
|
1475
|
+
else char = SHADES[SHADES.length - 1 - shadeIndex];
|
|
1476
|
+
}
|
|
1477
|
+
row += char;
|
|
1478
|
+
}
|
|
1479
|
+
rows.push(row);
|
|
1480
|
+
}
|
|
1481
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1482
|
+
flexDirection: "column",
|
|
1483
|
+
flexGrow: 1,
|
|
1484
|
+
children: rows.map((row, i) => /* @__PURE__ */ jsx(Text, {
|
|
1485
|
+
dimColor: true,
|
|
1486
|
+
children: row
|
|
1487
|
+
}, i))
|
|
1488
|
+
});
|
|
1489
|
+
};
|
|
1490
|
+
//#endregion
|
|
1491
|
+
//#region src/ui/tui/primitives/KeyboardHintsBar.tsx
|
|
1492
|
+
/**
|
|
1493
|
+
* KeyboardHintsBar — Row showing active keyboard shortcuts.
|
|
1494
|
+
*
|
|
1495
|
+
* Always reserves its row to prevent layout shift. When hints are
|
|
1496
|
+
* visible, renders them in dimmed grey text. When dismissed, renders
|
|
1497
|
+
* an empty reserved row.
|
|
1498
|
+
*/
|
|
1499
|
+
const KeyboardHintsBar = () => {
|
|
1500
|
+
const { hints, visible } = useKeyboardHintsContext();
|
|
1501
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1502
|
+
height: 1,
|
|
1503
|
+
paddingX: 1,
|
|
1504
|
+
children: visible && hints.length > 0 && hints.map((hint, i) => /* @__PURE__ */ jsxs(Box, {
|
|
1505
|
+
marginRight: i < hints.length - 1 ? 2 : 0,
|
|
1506
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
1507
|
+
bold: true,
|
|
1508
|
+
color: Colors.muted,
|
|
1509
|
+
children: hint.label
|
|
1510
|
+
}), /* @__PURE__ */ jsxs(Text, {
|
|
1511
|
+
dimColor: true,
|
|
1512
|
+
children: [" ", hint.action]
|
|
1513
|
+
})]
|
|
1514
|
+
}, `${hint.label}-${hint.action}`))
|
|
1515
|
+
});
|
|
1516
|
+
};
|
|
1517
|
+
//#endregion
|
|
1518
|
+
//#region src/ui/tui/primitives/ScreenErrorBoundary.tsx
|
|
1519
|
+
/**
|
|
1520
|
+
* ScreenErrorBoundary — catches React render errors in screens
|
|
1521
|
+
* and routes to the outro screen with an error message.
|
|
1522
|
+
*
|
|
1523
|
+
* Without this, a screen crash silently hangs the TUI.
|
|
1524
|
+
*/
|
|
1525
|
+
var ScreenErrorBoundary = class extends Component {
|
|
1526
|
+
state = { error: null };
|
|
1527
|
+
static getDerivedStateFromError(error) {
|
|
1528
|
+
return { error };
|
|
1529
|
+
}
|
|
1530
|
+
componentDidCatch(error) {
|
|
1531
|
+
const { store } = this.props;
|
|
1532
|
+
console.error("[ScreenErrorBoundary]", error.message, error.stack);
|
|
1533
|
+
store.setOutroData({
|
|
1534
|
+
kind: "error",
|
|
1535
|
+
message: `A screen crashed: ${error.message}`
|
|
1536
|
+
});
|
|
1537
|
+
store.setRunPhase("error");
|
|
1538
|
+
}
|
|
1539
|
+
render() {
|
|
1540
|
+
if (this.state.error) return /* @__PURE__ */ jsxs(Box, {
|
|
1541
|
+
flexDirection: "column",
|
|
1542
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
1543
|
+
color: "red",
|
|
1544
|
+
bold: true,
|
|
1545
|
+
children: "Something went wrong."
|
|
1546
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
1547
|
+
dimColor: true,
|
|
1548
|
+
children: this.state.error.message
|
|
1549
|
+
})]
|
|
1550
|
+
});
|
|
1551
|
+
return this.props.children;
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
//#endregion
|
|
1555
|
+
//#region src/ui/tui/primitives/ScreenContainer.tsx
|
|
1556
|
+
/**
|
|
1557
|
+
* ScreenContainer — Renders TitleBar + routes between screens with transitions.
|
|
1558
|
+
* Takes a screens map and renders the one matching store.currentScreen.
|
|
1559
|
+
* Horizontal wipe plays on push (left) or pop (right).
|
|
1560
|
+
*
|
|
1561
|
+
* Each screen is wrapped in a ScreenErrorBoundary so that render crashes
|
|
1562
|
+
* route to the outro screen with an error message instead of hanging.
|
|
1563
|
+
*
|
|
1564
|
+
* Provides KeyboardHintsProvider context. The hints bar is rendered below
|
|
1565
|
+
* screen content (inside the transition area) so all screens get it.
|
|
1566
|
+
*/
|
|
1567
|
+
const MIN_WIDTH = 80;
|
|
1568
|
+
const MAX_WIDTH = 120;
|
|
1569
|
+
/** Use terminal width when small so we don't overflow; otherwise clamp to [MIN_WIDTH, MAX_WIDTH]. */
|
|
1570
|
+
function getContentWidth(terminalColumns) {
|
|
1571
|
+
if (terminalColumns < MIN_WIDTH) return terminalColumns;
|
|
1572
|
+
return Math.min(MAX_WIDTH, terminalColumns);
|
|
1573
|
+
}
|
|
1574
|
+
const ScreenContainer = ({ store, screens }) => {
|
|
1575
|
+
const [columns, rows] = useStdoutDimensions();
|
|
1576
|
+
useSyncExternalStore((cb) => store.subscribe(cb), () => store.getSnapshot());
|
|
1577
|
+
const terminalWidth = columns;
|
|
1578
|
+
const width = getContentWidth(terminalWidth);
|
|
1579
|
+
const contentHeight = Math.max(5, rows - 3);
|
|
1580
|
+
const contentAreaWidth = Math.max(10, width - 2);
|
|
1581
|
+
const direction = store.lastNavDirection === "pop" ? "right" : "left";
|
|
1582
|
+
const activeScreen = screens[store.currentScreen] ?? null;
|
|
1583
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1584
|
+
flexDirection: "column",
|
|
1585
|
+
height: rows,
|
|
1586
|
+
width: terminalWidth,
|
|
1587
|
+
alignItems: "center",
|
|
1588
|
+
justifyContent: "flex-start",
|
|
1589
|
+
children: /* @__PURE__ */ jsx(KeyboardHintsProvider, { children: /* @__PURE__ */ jsxs(Box, {
|
|
1590
|
+
flexDirection: "column",
|
|
1591
|
+
height: rows,
|
|
1592
|
+
width,
|
|
1593
|
+
children: [
|
|
1594
|
+
/* @__PURE__ */ jsx(TitleBar, {
|
|
1595
|
+
version: store.version,
|
|
1596
|
+
width
|
|
1597
|
+
}),
|
|
1598
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1599
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1600
|
+
flexDirection: "column",
|
|
1601
|
+
flexGrow: 1,
|
|
1602
|
+
paddingX: 1,
|
|
1603
|
+
children: /* @__PURE__ */ jsx(DissolveTransition, {
|
|
1604
|
+
transitionKey: store.currentScreen,
|
|
1605
|
+
width: contentAreaWidth,
|
|
1606
|
+
height: contentHeight,
|
|
1607
|
+
direction,
|
|
1608
|
+
children: /* @__PURE__ */ jsx(ScreenErrorBoundary, {
|
|
1609
|
+
store,
|
|
1610
|
+
children: /* @__PURE__ */ jsxs(Box, {
|
|
1611
|
+
flexDirection: "column",
|
|
1612
|
+
height: contentHeight,
|
|
1613
|
+
children: [
|
|
1614
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1615
|
+
flexDirection: "column",
|
|
1616
|
+
flexGrow: 1,
|
|
1617
|
+
flexShrink: 1,
|
|
1618
|
+
overflow: "hidden",
|
|
1619
|
+
children: activeScreen
|
|
1620
|
+
}),
|
|
1621
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1622
|
+
/* @__PURE__ */ jsx(KeyboardHintsBar, {})
|
|
1623
|
+
]
|
|
1624
|
+
})
|
|
1625
|
+
})
|
|
1626
|
+
})
|
|
1627
|
+
})
|
|
1628
|
+
]
|
|
1629
|
+
}) })
|
|
1630
|
+
});
|
|
1631
|
+
};
|
|
1632
|
+
const TabContainer = ({ tabs, statusMessage, expandableStatus = false, store }) => {
|
|
1633
|
+
const [activeTab, setActiveTab] = useState(0);
|
|
1634
|
+
const [localExpanded, setLocalExpanded] = useState(false);
|
|
1635
|
+
const statusExpanded = store ? store.statusExpanded : localExpanded;
|
|
1636
|
+
useKeyBindings("tab-container", useMemo(() => {
|
|
1637
|
+
const b = [{
|
|
1638
|
+
match: ["leftArrow", "rightArrow"],
|
|
1639
|
+
label: "←→",
|
|
1640
|
+
action: "switch tab",
|
|
1641
|
+
handler: (_input, key) => {
|
|
1642
|
+
if (key.leftArrow) setActiveTab((prev) => Math.max(0, prev - 1));
|
|
1643
|
+
if (key.rightArrow) setActiveTab((prev) => Math.min(tabs.length - 1, prev + 1));
|
|
1644
|
+
}
|
|
1645
|
+
}];
|
|
1646
|
+
if (expandableStatus) b.push({
|
|
1647
|
+
match: "s",
|
|
1648
|
+
label: "s",
|
|
1649
|
+
action: "toggle status",
|
|
1650
|
+
priority: 12,
|
|
1651
|
+
handler: () => {
|
|
1652
|
+
if (store) store.toggleStatusExpanded();
|
|
1653
|
+
else setLocalExpanded((prev) => !prev);
|
|
1654
|
+
}
|
|
1655
|
+
});
|
|
1656
|
+
return b;
|
|
1657
|
+
}, [
|
|
1658
|
+
tabs.length,
|
|
1659
|
+
expandableStatus,
|
|
1660
|
+
store
|
|
1661
|
+
]));
|
|
1662
|
+
const current = tabs[activeTab];
|
|
1663
|
+
const allMessages = statusMessage ? Array.isArray(statusMessage) ? statusMessage : [statusMessage] : [];
|
|
1664
|
+
const visibleCount = expandableStatus && statusExpanded ? 10 : 2;
|
|
1665
|
+
const visibleMessages = allMessages.slice(-visibleCount);
|
|
1666
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1667
|
+
flexDirection: "column",
|
|
1668
|
+
flexGrow: 1,
|
|
1669
|
+
children: [
|
|
1670
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1671
|
+
flexDirection: "column",
|
|
1672
|
+
flexGrow: 1,
|
|
1673
|
+
flexShrink: 1,
|
|
1674
|
+
overflow: "hidden",
|
|
1675
|
+
children: current?.component
|
|
1676
|
+
}),
|
|
1677
|
+
visibleMessages.length > 0 && /* @__PURE__ */ jsx(Box, {
|
|
1678
|
+
flexDirection: "column",
|
|
1679
|
+
borderStyle: "single",
|
|
1680
|
+
borderTop: true,
|
|
1681
|
+
borderBottom: false,
|
|
1682
|
+
borderLeft: false,
|
|
1683
|
+
borderRight: false,
|
|
1684
|
+
borderColor: Colors.muted,
|
|
1685
|
+
paddingX: 1,
|
|
1686
|
+
overflow: "hidden",
|
|
1687
|
+
children: visibleMessages.map((msg, i, arr) => {
|
|
1688
|
+
const isCurrent = i === arr.length - 1;
|
|
1689
|
+
return /* @__PURE__ */ jsxs(Text, {
|
|
1690
|
+
color: Colors.muted,
|
|
1691
|
+
dimColor: !isCurrent,
|
|
1692
|
+
children: [
|
|
1693
|
+
isCurrent ? Icons.diamond : "┊",
|
|
1694
|
+
" ",
|
|
1695
|
+
msg
|
|
1696
|
+
]
|
|
1697
|
+
}, i);
|
|
1698
|
+
})
|
|
1699
|
+
}),
|
|
1700
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1701
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1702
|
+
gap: 1,
|
|
1703
|
+
paddingX: 1,
|
|
1704
|
+
children: tabs.map((tab, i) => /* @__PURE__ */ jsx(Text, {
|
|
1705
|
+
inverse: i === activeTab,
|
|
1706
|
+
color: i === activeTab ? Colors.accent : Colors.muted,
|
|
1707
|
+
bold: i === activeTab,
|
|
1708
|
+
children: ` ${tab.label} `
|
|
1709
|
+
}, tab.id))
|
|
1710
|
+
})
|
|
1711
|
+
]
|
|
1712
|
+
});
|
|
1713
|
+
};
|
|
1714
|
+
//#endregion
|
|
1715
|
+
//#region src/ui/tui/primitives/HNViewer.tsx
|
|
1716
|
+
/**
|
|
1717
|
+
* HNViewer — Top 10 Hacker News stories.
|
|
1718
|
+
*
|
|
1719
|
+
* Fetches from the HN Firebase API on mount.
|
|
1720
|
+
* Each story has a [1]–[0] numeral; typing it opens the HN comments page.
|
|
1721
|
+
*/
|
|
1722
|
+
const HN_API = "https://hacker-news.firebaseio.com/v0";
|
|
1723
|
+
const HNViewer = () => {
|
|
1724
|
+
const [stories, setStories] = useState([]);
|
|
1725
|
+
const [loading, setLoading] = useState(true);
|
|
1726
|
+
useEffect(() => {
|
|
1727
|
+
(async () => {
|
|
1728
|
+
try {
|
|
1729
|
+
const top10 = (await (await fetch(`${HN_API}/topstories.json`)).json()).slice(0, 10);
|
|
1730
|
+
setStories(await Promise.all(top10.map(async (id) => {
|
|
1731
|
+
return (await fetch(`${HN_API}/item/${id}.json`)).json();
|
|
1732
|
+
})));
|
|
1733
|
+
} catch {}
|
|
1734
|
+
setLoading(false);
|
|
1735
|
+
})();
|
|
1736
|
+
}, []);
|
|
1737
|
+
useKeyBindings("hn-viewer", [{
|
|
1738
|
+
match: [
|
|
1739
|
+
"1",
|
|
1740
|
+
"2",
|
|
1741
|
+
"3",
|
|
1742
|
+
"4",
|
|
1743
|
+
"5",
|
|
1744
|
+
"6",
|
|
1745
|
+
"7",
|
|
1746
|
+
"8",
|
|
1747
|
+
"9",
|
|
1748
|
+
"0"
|
|
1749
|
+
],
|
|
1750
|
+
label: "number keys",
|
|
1751
|
+
action: "open story",
|
|
1752
|
+
priority: 5,
|
|
1753
|
+
handler: (input) => openStory(input, stories)
|
|
1754
|
+
}]);
|
|
1755
|
+
if (loading) return /* @__PURE__ */ jsx(Box, {
|
|
1756
|
+
paddingX: 1,
|
|
1757
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1758
|
+
dimColor: true,
|
|
1759
|
+
children: "Loading Hacker News..."
|
|
1760
|
+
})
|
|
1761
|
+
});
|
|
1762
|
+
if (stories.length === 0) return /* @__PURE__ */ jsx(Box, {
|
|
1763
|
+
paddingX: 1,
|
|
1764
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1765
|
+
dimColor: true,
|
|
1766
|
+
children: "Could not load Hacker News."
|
|
1767
|
+
})
|
|
1768
|
+
});
|
|
1769
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1770
|
+
flexDirection: "column",
|
|
1771
|
+
paddingX: 1,
|
|
1772
|
+
children: [
|
|
1773
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1774
|
+
bold: true,
|
|
1775
|
+
color: Colors.accent,
|
|
1776
|
+
children: "Hacker News — Top 10"
|
|
1777
|
+
}),
|
|
1778
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
1779
|
+
stories.map((story, i) => {
|
|
1780
|
+
const key = i === 9 ? "0" : String(i + 1);
|
|
1781
|
+
const dateStr = (/* @__PURE__ */ new Date(story.time * 1e3)).toLocaleDateString("en-US", {
|
|
1782
|
+
month: "short",
|
|
1783
|
+
day: "numeric"
|
|
1784
|
+
});
|
|
1785
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1786
|
+
flexDirection: "column",
|
|
1787
|
+
children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
1788
|
+
color: Colors.accent,
|
|
1789
|
+
bold: true,
|
|
1790
|
+
children: [
|
|
1791
|
+
"[",
|
|
1792
|
+
key,
|
|
1793
|
+
"]"
|
|
1794
|
+
]
|
|
1795
|
+
}), /* @__PURE__ */ jsxs(Text, {
|
|
1796
|
+
bold: true,
|
|
1797
|
+
children: [" ", story.title]
|
|
1798
|
+
})] }), /* @__PURE__ */ jsx(Box, {
|
|
1799
|
+
marginLeft: 4,
|
|
1800
|
+
children: /* @__PURE__ */ jsxs(Text, {
|
|
1801
|
+
dimColor: true,
|
|
1802
|
+
children: [
|
|
1803
|
+
story.score,
|
|
1804
|
+
"pts • ",
|
|
1805
|
+
story.by,
|
|
1806
|
+
", ",
|
|
1807
|
+
dateStr
|
|
1808
|
+
]
|
|
1809
|
+
})
|
|
1810
|
+
})]
|
|
1811
|
+
}, story.id);
|
|
1812
|
+
})
|
|
1813
|
+
]
|
|
1814
|
+
});
|
|
1815
|
+
};
|
|
1816
|
+
function openStory(input, stories) {
|
|
1817
|
+
const num = parseInt(input, 10);
|
|
1818
|
+
if (isNaN(num)) return;
|
|
1819
|
+
const story = stories[num === 0 ? 9 : num - 1];
|
|
1820
|
+
if (!story) return;
|
|
1821
|
+
const url = `https://news.ycombinator.com/item?id=${story.id}`;
|
|
1822
|
+
import("child_process").then(({ exec }) => {
|
|
1823
|
+
exec(`open "${url}" 2>/dev/null || xdg-open "${url}" 2>/dev/null`);
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
//#endregion
|
|
1827
|
+
//#region src/ui/tui/primitives/text-helpers.ts
|
|
1828
|
+
/**
|
|
1829
|
+
* Text-splitting helpers for sentence boundary detection.
|
|
1830
|
+
* Used by TextBlock for animation pauses at punctuation.
|
|
1831
|
+
*/
|
|
1832
|
+
/** Split text into sentences (keeps the delimiter attached) */
|
|
1833
|
+
function splitSentences(text) {
|
|
1834
|
+
const parts = [];
|
|
1835
|
+
const re = /[^.!?]*[.!?]+\s*/g;
|
|
1836
|
+
let match;
|
|
1837
|
+
let lastIndex = 0;
|
|
1838
|
+
while ((match = re.exec(text)) !== null) {
|
|
1839
|
+
parts.push(match[0]);
|
|
1840
|
+
lastIndex = re.lastIndex;
|
|
1841
|
+
}
|
|
1842
|
+
if (lastIndex < text.length) parts.push(text.slice(lastIndex));
|
|
1843
|
+
return parts;
|
|
1844
|
+
}
|
|
1845
|
+
/** Build a set of character indices where sentences end (for typewriter pause) */
|
|
1846
|
+
function sentenceEndChars(text) {
|
|
1847
|
+
const ends = /* @__PURE__ */ new Set();
|
|
1848
|
+
const sentences = splitSentences(text);
|
|
1849
|
+
let pos = 0;
|
|
1850
|
+
for (const s of sentences) {
|
|
1851
|
+
pos += s.length;
|
|
1852
|
+
ends.add(pos - 1);
|
|
1853
|
+
}
|
|
1854
|
+
return ends;
|
|
1855
|
+
}
|
|
1856
|
+
/** Build a set of word indices where sentences end (for word-by-word pause) */
|
|
1857
|
+
function sentenceEndWords(text) {
|
|
1858
|
+
const ends = /* @__PURE__ */ new Set();
|
|
1859
|
+
const sentences = splitSentences(text);
|
|
1860
|
+
let wordCount = 0;
|
|
1861
|
+
for (const s of sentences) {
|
|
1862
|
+
const words = s.trim().split(/\s+/).filter(Boolean);
|
|
1863
|
+
wordCount += words.length;
|
|
1864
|
+
ends.add(wordCount - 1);
|
|
1865
|
+
}
|
|
1866
|
+
return ends;
|
|
1867
|
+
}
|
|
1868
|
+
//#endregion
|
|
1869
|
+
//#region src/ui/tui/primitives/content-types.ts
|
|
1870
|
+
/** Type guard for lines blocks. */
|
|
1871
|
+
function isLinesBlock(block) {
|
|
1872
|
+
return typeof block !== "string" && "type" in block && block.type === "lines";
|
|
1873
|
+
}
|
|
1874
|
+
/** Type guard for clear blocks. */
|
|
1875
|
+
function isClearBlock(block) {
|
|
1876
|
+
return typeof block !== "string" && "type" in block && block.type === "clear";
|
|
1877
|
+
}
|
|
1878
|
+
/** Type guard for object blocks (text or node content). */
|
|
1879
|
+
function isObjectBlock(block) {
|
|
1880
|
+
return typeof block !== "string" && !("type" in block);
|
|
1881
|
+
}
|
|
1882
|
+
//#endregion
|
|
1883
|
+
//#region src/ui/tui/primitives/layout-helpers.ts
|
|
1884
|
+
/**
|
|
1885
|
+
* Estimate the number of terminal rows a content block will occupy,
|
|
1886
|
+
* including 1 row of marginBottom.
|
|
1887
|
+
*/
|
|
1888
|
+
function estimateBlockHeight(block, availableWidth) {
|
|
1889
|
+
if (typeof block === "string") return wordWrap(block, availableWidth).length + 1;
|
|
1890
|
+
if (isClearBlock(block)) return 0;
|
|
1891
|
+
if (isLinesBlock(block)) return block.lines.length + 1;
|
|
1892
|
+
if (isObjectBlock(block)) {
|
|
1893
|
+
if (typeof block.content === "string") return wordWrap(block.content, availableWidth).length + 1;
|
|
1894
|
+
return 4;
|
|
1895
|
+
}
|
|
1896
|
+
return 1;
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Given all blocks, the active index, available width, and a row budget,
|
|
1900
|
+
* return [startIdx, endIdx] — the range of blocks to render.
|
|
1901
|
+
*
|
|
1902
|
+
* Always includes activeIdx. Walks backward to include as many completed
|
|
1903
|
+
* blocks as fit within maxHeight.
|
|
1904
|
+
*/
|
|
1905
|
+
function computeVisibleRange(blocks, activeIdx, availableWidth, maxHeight) {
|
|
1906
|
+
const budget = Math.max(4, maxHeight - 2);
|
|
1907
|
+
let totalHeight = estimateBlockHeight(blocks[activeIdx], availableWidth);
|
|
1908
|
+
let start = activeIdx;
|
|
1909
|
+
for (let i = activeIdx - 1; i >= 0; i--) {
|
|
1910
|
+
if (isClearBlock(blocks[i])) break;
|
|
1911
|
+
const h = estimateBlockHeight(blocks[i], availableWidth);
|
|
1912
|
+
if (totalHeight + h > budget) break;
|
|
1913
|
+
totalHeight += h;
|
|
1914
|
+
start = i;
|
|
1915
|
+
}
|
|
1916
|
+
return [start, activeIdx];
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Word-wrap text at clean word boundaries. Always returns pre-wrapped text
|
|
1920
|
+
* joined with \n — this avoids Ink's native wrap which can leave leading
|
|
1921
|
+
* spaces on continuation lines.
|
|
1922
|
+
*
|
|
1923
|
+
* Uses a 1-char safety margin so slight width estimate mismatches don't
|
|
1924
|
+
* cause Ink to re-wrap our already-wrapped lines.
|
|
1925
|
+
*/
|
|
1926
|
+
function wordWrap(text, availableWidth) {
|
|
1927
|
+
const safeWidth = Math.max(10, availableWidth - 1);
|
|
1928
|
+
const words = text.split(/\s+/);
|
|
1929
|
+
const lines = [];
|
|
1930
|
+
let currentLine = "";
|
|
1931
|
+
for (const word of words) if (currentLine.length + word.length + 1 > safeWidth && currentLine.length > 0) {
|
|
1932
|
+
lines.push(currentLine);
|
|
1933
|
+
currentLine = word;
|
|
1934
|
+
} else currentLine += (currentLine.length > 0 ? " " : "") + word;
|
|
1935
|
+
if (currentLine.length > 0) lines.push(currentLine);
|
|
1936
|
+
return lines;
|
|
1937
|
+
}
|
|
1938
|
+
/**
|
|
1939
|
+
* Word-wrap text and return only the last `maxRows` lines.
|
|
1940
|
+
* Used for intra-block truncation when a single text block exceeds the viewport.
|
|
1941
|
+
* Also used for normal rendering to avoid Ink's leading-space wrap artifacts.
|
|
1942
|
+
*/
|
|
1943
|
+
function wrapAndTruncate(text, availableWidth, maxRows) {
|
|
1944
|
+
const lines = wordWrap(text, availableWidth);
|
|
1945
|
+
if (lines.length <= maxRows) return lines.join("\n");
|
|
1946
|
+
return lines.slice(-maxRows).join("\n");
|
|
1947
|
+
}
|
|
1948
|
+
//#endregion
|
|
1949
|
+
//#region src/ui/tui/primitives/TextBlock.tsx
|
|
1950
|
+
/**
|
|
1951
|
+
* TextBlock — Animates a single string paragraph.
|
|
1952
|
+
*
|
|
1953
|
+
* Self-contained: owns its own animIdx and timer.
|
|
1954
|
+
* Calls onComplete() when the animation finishes.
|
|
1955
|
+
*
|
|
1956
|
+
* Five animation modes:
|
|
1957
|
+
* 1. Typewriter — character-by-character reveal
|
|
1958
|
+
* 2. Word by word — each word appears in order
|
|
1959
|
+
* 3. Sentence by sentence — sentences appear one at a time
|
|
1960
|
+
* 4. Paragraph fade — paragraph appears at full opacity immediately
|
|
1961
|
+
* 5. Sentence fade — paragraph dim, sentences light up in order
|
|
1962
|
+
*/
|
|
1963
|
+
/** Default interval per mode (ms) */
|
|
1964
|
+
const TEXT_REVEAL_MODE_DEFAULTS = {
|
|
1965
|
+
[1]: 240,
|
|
1966
|
+
[0]: 32,
|
|
1967
|
+
[2]: 1800,
|
|
1968
|
+
[3]: 4800,
|
|
1969
|
+
[4]: 2400
|
|
1970
|
+
};
|
|
1971
|
+
const TextBlock = ({ text, active, completed, onComplete, mode, bullet, animationInterval, sentenceInterval = 1600, maxHeight, availableWidth }) => {
|
|
1972
|
+
const speed = animationInterval ?? TEXT_REVEAL_MODE_DEFAULTS[mode];
|
|
1973
|
+
const [animIdx, setAnimIdx] = useState(mode === 4 ? 1 : 0);
|
|
1974
|
+
const resetRef = useRef(0);
|
|
1975
|
+
const prevMode = useRef(mode);
|
|
1976
|
+
if (prevMode.current !== mode) {
|
|
1977
|
+
prevMode.current = mode;
|
|
1978
|
+
resetRef.current += 1;
|
|
1979
|
+
setAnimIdx(mode === 4 ? 1 : 0);
|
|
1980
|
+
}
|
|
1981
|
+
const words = text.split(/\s+/);
|
|
1982
|
+
const sentences = splitSentences(text);
|
|
1983
|
+
const sentenceCharEnds = useMemo(() => sentenceEndChars(text), [text]);
|
|
1984
|
+
const sentenceWordEnds = useMemo(() => sentenceEndWords(text), [text]);
|
|
1985
|
+
const isDone = mode === 0 ? animIdx >= text.length : mode === 3 ? true : mode === 1 ? animIdx >= words.length : mode === 4 || mode === 2 ? animIdx >= sentences.length : true;
|
|
1986
|
+
useEffect(() => {
|
|
1987
|
+
if (isDone && active) onComplete();
|
|
1988
|
+
}, [
|
|
1989
|
+
isDone,
|
|
1990
|
+
active,
|
|
1991
|
+
onComplete
|
|
1992
|
+
]);
|
|
1993
|
+
useEffect(() => {
|
|
1994
|
+
if (!active || mode === 3 || isDone) return;
|
|
1995
|
+
const token = resetRef.current;
|
|
1996
|
+
const isFirstTick = animIdx === 0;
|
|
1997
|
+
let delay = isFirstTick ? 0 : speed;
|
|
1998
|
+
if (!isFirstTick && mode === 0 && animIdx > 0 && sentenceCharEnds.has(animIdx - 1)) delay = sentenceInterval;
|
|
1999
|
+
else if (!isFirstTick && mode === 1 && animIdx > 0 && sentenceWordEnds.has(animIdx - 1)) delay = sentenceInterval;
|
|
2000
|
+
const timer = setTimeout(() => {
|
|
2001
|
+
if (token !== resetRef.current) return;
|
|
2002
|
+
setAnimIdx((c) => c + 1);
|
|
2003
|
+
}, delay);
|
|
2004
|
+
return () => clearTimeout(timer);
|
|
2005
|
+
}, [
|
|
2006
|
+
active,
|
|
2007
|
+
mode,
|
|
2008
|
+
animIdx,
|
|
2009
|
+
isDone,
|
|
2010
|
+
speed,
|
|
2011
|
+
sentenceInterval,
|
|
2012
|
+
sentenceCharEnds,
|
|
2013
|
+
sentenceWordEnds
|
|
2014
|
+
]);
|
|
2015
|
+
const wrap = (visibleText) => {
|
|
2016
|
+
if (availableWidth == null) return visibleText;
|
|
2017
|
+
if (maxHeight == null) return wrapAndTruncate(visibleText, availableWidth, Infinity);
|
|
2018
|
+
return wrapAndTruncate(visibleText, availableWidth, maxHeight);
|
|
2019
|
+
};
|
|
2020
|
+
if (completed) return /* @__PURE__ */ jsxs(Text, {
|
|
2021
|
+
dimColor: true,
|
|
2022
|
+
children: [bullet, wrap(text)]
|
|
2023
|
+
});
|
|
2024
|
+
if (mode === 0) {
|
|
2025
|
+
const revealed = text.slice(0, animIdx);
|
|
2026
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
2027
|
+
bullet,
|
|
2028
|
+
wrap(/[.!?]\s*$/.test(revealed) ? revealed.trimEnd() : revealed),
|
|
2029
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2030
|
+
color: Colors.muted,
|
|
2031
|
+
children: "▌"
|
|
2032
|
+
})
|
|
2033
|
+
] });
|
|
2034
|
+
}
|
|
2035
|
+
if (mode === 1) return /* @__PURE__ */ jsxs(Text, { children: [bullet, wrap(words.slice(0, animIdx).join(" "))] });
|
|
2036
|
+
if (mode === 3) return /* @__PURE__ */ jsxs(Text, { children: [bullet, wrap(text)] });
|
|
2037
|
+
if (mode === 2) return /* @__PURE__ */ jsxs(Text, { children: [bullet, wrap(sentences.slice(0, animIdx).join(""))] });
|
|
2038
|
+
return /* @__PURE__ */ jsxs(Text, { children: [bullet, sentences.map((s, si) => /* @__PURE__ */ jsx(Text, {
|
|
2039
|
+
dimColor: si >= animIdx,
|
|
2040
|
+
children: s
|
|
2041
|
+
}, si))] });
|
|
2042
|
+
};
|
|
2043
|
+
//#endregion
|
|
2044
|
+
//#region src/ui/tui/primitives/LinesBlock.tsx
|
|
2045
|
+
/**
|
|
2046
|
+
* LinesBlock — Reveals ReactNode lines one at a time.
|
|
2047
|
+
* Each line can contain colors, bold, ASCII art — any JSX.
|
|
2048
|
+
*/
|
|
2049
|
+
const LinesBlock = ({ lines, interval, active, completed, onComplete, maxHeight }) => {
|
|
2050
|
+
const [revealedCount, setRevealedCount] = useState(0);
|
|
2051
|
+
useEffect(() => {
|
|
2052
|
+
if (!active || revealedCount >= lines.length) return;
|
|
2053
|
+
const timer = setTimeout(() => setRevealedCount((c) => c + 1), revealedCount === 0 ? 0 : interval);
|
|
2054
|
+
return () => clearTimeout(timer);
|
|
2055
|
+
}, [
|
|
2056
|
+
active,
|
|
2057
|
+
revealedCount,
|
|
2058
|
+
lines.length,
|
|
2059
|
+
interval
|
|
2060
|
+
]);
|
|
2061
|
+
useEffect(() => {
|
|
2062
|
+
if (active && revealedCount >= lines.length) onComplete();
|
|
2063
|
+
}, [
|
|
2064
|
+
active,
|
|
2065
|
+
revealedCount,
|
|
2066
|
+
lines.length,
|
|
2067
|
+
onComplete
|
|
2068
|
+
]);
|
|
2069
|
+
const visibleStart = maxHeight != null ? Math.max(0, (completed ? lines.length : revealedCount) - maxHeight) : 0;
|
|
2070
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
2071
|
+
flexDirection: "column",
|
|
2072
|
+
children: lines.map((line, li) => {
|
|
2073
|
+
if (completed) {
|
|
2074
|
+
if (li < visibleStart) return null;
|
|
2075
|
+
return /* @__PURE__ */ jsx(Box, { children: line }, li);
|
|
2076
|
+
}
|
|
2077
|
+
if (li >= revealedCount || li < visibleStart) return null;
|
|
2078
|
+
return /* @__PURE__ */ jsx(Box, { children: line }, li);
|
|
2079
|
+
})
|
|
2080
|
+
});
|
|
2081
|
+
};
|
|
2082
|
+
//#endregion
|
|
2083
|
+
//#region src/ui/tui/primitives/NodeBlock.tsx
|
|
2084
|
+
/**
|
|
2085
|
+
* NodeBlock — Renders static JSX, fires onComplete immediately.
|
|
2086
|
+
* The sequencer's blockInterval handles dwell time.
|
|
2087
|
+
*/
|
|
2088
|
+
const NodeBlock = ({ content, active, completed, onComplete }) => {
|
|
2089
|
+
useEffect(() => {
|
|
2090
|
+
if (active) onComplete();
|
|
2091
|
+
}, [active, onComplete]);
|
|
2092
|
+
if (completed) return /* @__PURE__ */ jsx(Text, {
|
|
2093
|
+
dimColor: true,
|
|
2094
|
+
children: content
|
|
2095
|
+
});
|
|
2096
|
+
return /* @__PURE__ */ jsx(Fragment, { children: content });
|
|
2097
|
+
};
|
|
2098
|
+
//#endregion
|
|
2099
|
+
//#region src/ui/tui/primitives/ContentSequencer.tsx
|
|
2100
|
+
/**
|
|
2101
|
+
* ContentSequencer — Plays content blocks in order.
|
|
2102
|
+
*
|
|
2103
|
+
* Each block is a self-animating component that fires onComplete() when done.
|
|
2104
|
+
* The sequencer waits blockInterval ms between blocks, then advances.
|
|
2105
|
+
*
|
|
2106
|
+
* Block types:
|
|
2107
|
+
* - string → TextBlock (animated text, sugar for { content: '...' })
|
|
2108
|
+
* - { content: str } → TextBlock (animated text with per-block overrides)
|
|
2109
|
+
* - { content: JSX } → NodeBlock (static JSX)
|
|
2110
|
+
* - { type: 'lines' } → LinesBlock (line-by-line reveal)
|
|
2111
|
+
* - { type: 'clear' } → ClearBlock (page break — hides all prior blocks)
|
|
2112
|
+
*/
|
|
2113
|
+
/** Resolve the pause after a block completes. */
|
|
2114
|
+
function getBlockPause(block, blockInterval) {
|
|
2115
|
+
if (typeof block === "string") return blockInterval;
|
|
2116
|
+
return block.pause ?? blockInterval;
|
|
2117
|
+
}
|
|
2118
|
+
const ContentSequencer = ({ blocks, mode, maxHeight, availableWidth, bullet, animationInterval, sentenceInterval, lineInterval = 200, blockInterval = 3200, startDelay = 0, initialBlockIdx = 0, onBlockChange, onSequenceComplete }) => {
|
|
2119
|
+
const resuming = initialBlockIdx > 0;
|
|
2120
|
+
const [activeIdx, setActiveIdx] = useState(resuming ? initialBlockIdx : startDelay > 0 ? -1 : 0);
|
|
2121
|
+
const transitionTimer = useRef(null);
|
|
2122
|
+
useEffect(() => {
|
|
2123
|
+
if (resuming || startDelay <= 0 || activeIdx !== -1) return;
|
|
2124
|
+
const timer = setTimeout(() => setActiveIdx(0), startDelay);
|
|
2125
|
+
return () => clearTimeout(timer);
|
|
2126
|
+
}, [startDelay, activeIdx]);
|
|
2127
|
+
const [visibleStart, visibleEnd] = useMemo(() => {
|
|
2128
|
+
if (activeIdx < 0) return [0, -1];
|
|
2129
|
+
if (maxHeight == null || availableWidth == null) return [0, activeIdx];
|
|
2130
|
+
return computeVisibleRange(blocks, activeIdx, availableWidth, maxHeight);
|
|
2131
|
+
}, [
|
|
2132
|
+
blocks,
|
|
2133
|
+
activeIdx,
|
|
2134
|
+
maxHeight,
|
|
2135
|
+
availableWidth
|
|
2136
|
+
]);
|
|
2137
|
+
const handleComplete = useCallback((blockIndex) => {
|
|
2138
|
+
if (blockIndex !== activeIdx) return;
|
|
2139
|
+
if (activeIdx >= blocks.length - 1) {
|
|
2140
|
+
if (onSequenceComplete && !transitionTimer.current) {
|
|
2141
|
+
const pause = getBlockPause(blocks[blockIndex], blockInterval);
|
|
2142
|
+
transitionTimer.current = setTimeout(() => {
|
|
2143
|
+
transitionTimer.current = null;
|
|
2144
|
+
onSequenceComplete();
|
|
2145
|
+
}, pause);
|
|
2146
|
+
}
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
if (transitionTimer.current) return;
|
|
2150
|
+
const pause = getBlockPause(blocks[blockIndex], blockInterval);
|
|
2151
|
+
transitionTimer.current = setTimeout(() => {
|
|
2152
|
+
transitionTimer.current = null;
|
|
2153
|
+
setActiveIdx((i) => {
|
|
2154
|
+
const next = i + 1;
|
|
2155
|
+
onBlockChange?.(next);
|
|
2156
|
+
return next;
|
|
2157
|
+
});
|
|
2158
|
+
}, pause);
|
|
2159
|
+
}, [
|
|
2160
|
+
activeIdx,
|
|
2161
|
+
blocks,
|
|
2162
|
+
blockInterval,
|
|
2163
|
+
onBlockChange,
|
|
2164
|
+
onSequenceComplete
|
|
2165
|
+
]);
|
|
2166
|
+
const clearFloor = useMemo(() => {
|
|
2167
|
+
if (activeIdx >= 0 && isClearBlock(blocks[activeIdx])) return activeIdx;
|
|
2168
|
+
for (let i = activeIdx - 1; i >= 0; i--) if (isClearBlock(blocks[i])) return i + 1;
|
|
2169
|
+
return 0;
|
|
2170
|
+
}, [blocks, activeIdx]);
|
|
2171
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
2172
|
+
flexDirection: "column",
|
|
2173
|
+
children: blocks.map((block, i) => {
|
|
2174
|
+
if (i > activeIdx) return null;
|
|
2175
|
+
if (i < clearFloor) return null;
|
|
2176
|
+
if (isClearBlock(block) && i < activeIdx) return null;
|
|
2177
|
+
if (i < visibleStart || i > visibleEnd) return null;
|
|
2178
|
+
const active = i === activeIdx;
|
|
2179
|
+
const completed = i < activeIdx;
|
|
2180
|
+
if (completed && isObjectBlock(block)) {
|
|
2181
|
+
const isText = typeof block.content === "string";
|
|
2182
|
+
if (!(block.persist ?? isText)) return null;
|
|
2183
|
+
}
|
|
2184
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
2185
|
+
flexDirection: "column",
|
|
2186
|
+
marginBottom: 1,
|
|
2187
|
+
children: /* @__PURE__ */ jsx(BlockRenderer, {
|
|
2188
|
+
block,
|
|
2189
|
+
active,
|
|
2190
|
+
completed,
|
|
2191
|
+
onComplete: () => handleComplete(i),
|
|
2192
|
+
mode,
|
|
2193
|
+
bullet,
|
|
2194
|
+
animationInterval,
|
|
2195
|
+
sentenceInterval,
|
|
2196
|
+
lineInterval,
|
|
2197
|
+
maxHeight,
|
|
2198
|
+
availableWidth
|
|
2199
|
+
})
|
|
2200
|
+
}, i);
|
|
2201
|
+
})
|
|
2202
|
+
});
|
|
2203
|
+
};
|
|
2204
|
+
const BlockRenderer = ({ block, active, completed, onComplete, mode, bullet, animationInterval, sentenceInterval, lineInterval, maxHeight, availableWidth }) => {
|
|
2205
|
+
if (isClearBlock(block)) {
|
|
2206
|
+
useEffect(() => {
|
|
2207
|
+
if (active) onComplete();
|
|
2208
|
+
}, [active, onComplete]);
|
|
2209
|
+
return null;
|
|
2210
|
+
}
|
|
2211
|
+
if (typeof block === "string") return /* @__PURE__ */ jsx(TextBlock, {
|
|
2212
|
+
text: block,
|
|
2213
|
+
active,
|
|
2214
|
+
completed,
|
|
2215
|
+
onComplete,
|
|
2216
|
+
mode,
|
|
2217
|
+
bullet,
|
|
2218
|
+
animationInterval,
|
|
2219
|
+
sentenceInterval,
|
|
2220
|
+
maxHeight,
|
|
2221
|
+
availableWidth
|
|
2222
|
+
});
|
|
2223
|
+
if (isLinesBlock(block)) return /* @__PURE__ */ jsx(LinesBlock, {
|
|
2224
|
+
lines: block.lines,
|
|
2225
|
+
interval: block.interval ?? lineInterval,
|
|
2226
|
+
active,
|
|
2227
|
+
completed,
|
|
2228
|
+
onComplete,
|
|
2229
|
+
maxHeight
|
|
2230
|
+
});
|
|
2231
|
+
if (typeof block.content === "string") return /* @__PURE__ */ jsx(TextBlock, {
|
|
2232
|
+
text: block.content,
|
|
2233
|
+
active,
|
|
2234
|
+
completed,
|
|
2235
|
+
onComplete,
|
|
2236
|
+
mode: block.mode ?? mode,
|
|
2237
|
+
bullet,
|
|
2238
|
+
animationInterval: block.animationInterval ?? animationInterval,
|
|
2239
|
+
sentenceInterval: block.sentenceInterval ?? sentenceInterval,
|
|
2240
|
+
maxHeight,
|
|
2241
|
+
availableWidth
|
|
2242
|
+
});
|
|
2243
|
+
return /* @__PURE__ */ jsx(NodeBlock, {
|
|
2244
|
+
content: block.content,
|
|
2245
|
+
active,
|
|
2246
|
+
completed,
|
|
2247
|
+
onComplete
|
|
2248
|
+
});
|
|
2249
|
+
};
|
|
2250
|
+
//#endregion
|
|
2251
|
+
//#region src/ui/tui/components/LearnCard.tsx
|
|
2252
|
+
/**
|
|
2253
|
+
* LearnCard — PostHog educational content with animated text reveal.
|
|
2254
|
+
*/
|
|
2255
|
+
/**
|
|
2256
|
+
* StatusPeekTrigger — Fires the status bar expansion once, renders nothing.
|
|
2257
|
+
* The peek is guarded by peekedRef so re-mounts are safe.
|
|
2258
|
+
*/
|
|
2259
|
+
const StatusPeekTrigger = ({ store, duration = 1e4, peekedRef }) => {
|
|
2260
|
+
useEffect(() => {
|
|
2261
|
+
if (peekedRef.current) return;
|
|
2262
|
+
peekedRef.current = true;
|
|
2263
|
+
store?.setStatusExpanded(true);
|
|
2264
|
+
setTimeout(() => {
|
|
2265
|
+
store?.setStatusExpanded(false);
|
|
2266
|
+
}, duration);
|
|
2267
|
+
}, [
|
|
2268
|
+
store,
|
|
2269
|
+
duration,
|
|
2270
|
+
peekedRef
|
|
2271
|
+
]);
|
|
2272
|
+
return /* @__PURE__ */ jsx(Text, { children: "You can view the Wizard's status below." });
|
|
2273
|
+
};
|
|
2274
|
+
const POSTHOG_DATA_FLOW = {
|
|
2275
|
+
type: "lines",
|
|
2276
|
+
interval: 500,
|
|
2277
|
+
pause: 8e3,
|
|
2278
|
+
lines: [
|
|
2279
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2280
|
+
color: "gray",
|
|
2281
|
+
children: " ┌──────────────────────────────┐"
|
|
2282
|
+
}),
|
|
2283
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2284
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2285
|
+
color: "gray",
|
|
2286
|
+
children: " │ "
|
|
2287
|
+
}),
|
|
2288
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2289
|
+
bold: true,
|
|
2290
|
+
color: "cyan",
|
|
2291
|
+
children: "Your App"
|
|
2292
|
+
}),
|
|
2293
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2294
|
+
color: "gray",
|
|
2295
|
+
children: " │"
|
|
2296
|
+
})
|
|
2297
|
+
] }),
|
|
2298
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2299
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2300
|
+
color: "gray",
|
|
2301
|
+
children: " │ │ "
|
|
2302
|
+
}),
|
|
2303
|
+
/* @__PURE__ */ jsx(Text, { children: "posthog.capture()" }),
|
|
2304
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2305
|
+
color: "gray",
|
|
2306
|
+
children: " │"
|
|
2307
|
+
})
|
|
2308
|
+
] }),
|
|
2309
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2310
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2311
|
+
color: "gray",
|
|
2312
|
+
children: " │ │ "
|
|
2313
|
+
}),
|
|
2314
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2315
|
+
dimColor: true,
|
|
2316
|
+
children: "custom events"
|
|
2317
|
+
}),
|
|
2318
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2319
|
+
color: "gray",
|
|
2320
|
+
children: " │"
|
|
2321
|
+
})
|
|
2322
|
+
] }),
|
|
2323
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2324
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2325
|
+
color: "gray",
|
|
2326
|
+
children: " │ │ "
|
|
2327
|
+
}),
|
|
2328
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2329
|
+
dimColor: true,
|
|
2330
|
+
children: "custom properties"
|
|
2331
|
+
}),
|
|
2332
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2333
|
+
color: "gray",
|
|
2334
|
+
children: " │"
|
|
2335
|
+
})
|
|
2336
|
+
] }),
|
|
2337
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2338
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2339
|
+
color: "gray",
|
|
2340
|
+
children: " │ │ "
|
|
2341
|
+
}),
|
|
2342
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2343
|
+
dimColor: true,
|
|
2344
|
+
children: "person profiles"
|
|
2345
|
+
}),
|
|
2346
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2347
|
+
color: "gray",
|
|
2348
|
+
children: " │"
|
|
2349
|
+
})
|
|
2350
|
+
] }),
|
|
2351
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2352
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2353
|
+
color: "gray",
|
|
2354
|
+
children: " │ ↓ "
|
|
2355
|
+
}),
|
|
2356
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2357
|
+
dimColor: true,
|
|
2358
|
+
children: "groups"
|
|
2359
|
+
}),
|
|
2360
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2361
|
+
color: "gray",
|
|
2362
|
+
children: " │"
|
|
2363
|
+
})
|
|
2364
|
+
] }),
|
|
2365
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2366
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2367
|
+
color: "gray",
|
|
2368
|
+
children: " │ "
|
|
2369
|
+
}),
|
|
2370
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2371
|
+
bold: true,
|
|
2372
|
+
color: Colors.accent,
|
|
2373
|
+
children: "PostHog SDK"
|
|
2374
|
+
}),
|
|
2375
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2376
|
+
color: "gray",
|
|
2377
|
+
children: " │"
|
|
2378
|
+
})
|
|
2379
|
+
] }),
|
|
2380
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2381
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2382
|
+
color: "gray",
|
|
2383
|
+
children: " │ ↓ "
|
|
2384
|
+
}),
|
|
2385
|
+
/* @__PURE__ */ jsx(Text, { children: "HTTP" }),
|
|
2386
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2387
|
+
color: "gray",
|
|
2388
|
+
children: " │"
|
|
2389
|
+
})
|
|
2390
|
+
] }),
|
|
2391
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2392
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2393
|
+
color: "gray",
|
|
2394
|
+
children: " │ "
|
|
2395
|
+
}),
|
|
2396
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2397
|
+
bold: true,
|
|
2398
|
+
color: Colors.accent,
|
|
2399
|
+
children: "PostHog Cloud"
|
|
2400
|
+
}),
|
|
2401
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2402
|
+
color: "gray",
|
|
2403
|
+
children: " │"
|
|
2404
|
+
})
|
|
2405
|
+
] }),
|
|
2406
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2407
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2408
|
+
color: "gray",
|
|
2409
|
+
children: " │ ↓ "
|
|
2410
|
+
}),
|
|
2411
|
+
/* @__PURE__ */ jsx(Text, { children: "query + visualize" }),
|
|
2412
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2413
|
+
color: "gray",
|
|
2414
|
+
children: " │"
|
|
2415
|
+
})
|
|
2416
|
+
] }),
|
|
2417
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2418
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2419
|
+
color: "gray",
|
|
2420
|
+
children: " │ "
|
|
2421
|
+
}),
|
|
2422
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2423
|
+
bold: true,
|
|
2424
|
+
color: "green",
|
|
2425
|
+
children: "Dashboards & Insights"
|
|
2426
|
+
}),
|
|
2427
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2428
|
+
color: "gray",
|
|
2429
|
+
children: " │"
|
|
2430
|
+
})
|
|
2431
|
+
] }),
|
|
2432
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2433
|
+
color: "gray",
|
|
2434
|
+
children: " └──────────────────────────────┘"
|
|
2435
|
+
})
|
|
2436
|
+
]
|
|
2437
|
+
};
|
|
2438
|
+
const PRODUCT_SUITE_BLOCK = {
|
|
2439
|
+
type: "lines",
|
|
2440
|
+
interval: 1e3,
|
|
2441
|
+
pause: 15e3,
|
|
2442
|
+
lines: [
|
|
2443
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2444
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2445
|
+
color: "cyan",
|
|
2446
|
+
children: " ◆ "
|
|
2447
|
+
}),
|
|
2448
|
+
"Product Analytics ",
|
|
2449
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2450
|
+
color: "cyan",
|
|
2451
|
+
children: "◆ "
|
|
2452
|
+
}),
|
|
2453
|
+
"Error Tracking"
|
|
2454
|
+
] }),
|
|
2455
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2456
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2457
|
+
color: "cyan",
|
|
2458
|
+
children: " ◆ "
|
|
2459
|
+
}),
|
|
2460
|
+
"Web Analytics ",
|
|
2461
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2462
|
+
color: "cyan",
|
|
2463
|
+
children: "◆ "
|
|
2464
|
+
}),
|
|
2465
|
+
"Session Replay"
|
|
2466
|
+
] }),
|
|
2467
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2468
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2469
|
+
color: "cyan",
|
|
2470
|
+
children: " ◆ "
|
|
2471
|
+
}),
|
|
2472
|
+
"Feature Flags ",
|
|
2473
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2474
|
+
color: "cyan",
|
|
2475
|
+
children: "◆ "
|
|
2476
|
+
}),
|
|
2477
|
+
"Data Pipelines"
|
|
2478
|
+
] }),
|
|
2479
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2480
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2481
|
+
color: "cyan",
|
|
2482
|
+
children: " ◆ "
|
|
2483
|
+
}),
|
|
2484
|
+
"Experiments ",
|
|
2485
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2486
|
+
color: "cyan",
|
|
2487
|
+
children: "◆ "
|
|
2488
|
+
}),
|
|
2489
|
+
"Data Warehouse"
|
|
2490
|
+
] }),
|
|
2491
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2492
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2493
|
+
color: "cyan",
|
|
2494
|
+
children: " ◆ "
|
|
2495
|
+
}),
|
|
2496
|
+
"LLM Analytics ",
|
|
2497
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2498
|
+
color: "cyan",
|
|
2499
|
+
children: "◆ "
|
|
2500
|
+
}),
|
|
2501
|
+
"Surveys"
|
|
2502
|
+
] }),
|
|
2503
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2504
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2505
|
+
color: "cyan",
|
|
2506
|
+
children: " ◆ "
|
|
2507
|
+
}),
|
|
2508
|
+
"Workflows ",
|
|
2509
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2510
|
+
color: "cyan",
|
|
2511
|
+
children: "◆ "
|
|
2512
|
+
}),
|
|
2513
|
+
"Logs"
|
|
2514
|
+
] }),
|
|
2515
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2516
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2517
|
+
color: "cyan",
|
|
2518
|
+
children: " ◆ "
|
|
2519
|
+
}),
|
|
2520
|
+
"Product Tours ",
|
|
2521
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2522
|
+
color: "cyan",
|
|
2523
|
+
children: "◆ "
|
|
2524
|
+
}),
|
|
2525
|
+
"Support"
|
|
2526
|
+
] }),
|
|
2527
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2528
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2529
|
+
color: "cyan",
|
|
2530
|
+
children: " ◆ "
|
|
2531
|
+
}),
|
|
2532
|
+
"Revenue Analytics ",
|
|
2533
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2534
|
+
color: "cyan",
|
|
2535
|
+
children: "◆ "
|
|
2536
|
+
}),
|
|
2537
|
+
"Endpoints"
|
|
2538
|
+
] }),
|
|
2539
|
+
/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2540
|
+
color: "cyan",
|
|
2541
|
+
children: " ◆ "
|
|
2542
|
+
}), "Customer Analytics"] })
|
|
2543
|
+
]
|
|
2544
|
+
};
|
|
2545
|
+
const LINE_CHART_BLOCK = {
|
|
2546
|
+
type: "lines",
|
|
2547
|
+
interval: 300,
|
|
2548
|
+
pause: 6e3,
|
|
2549
|
+
lines: [
|
|
2550
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2551
|
+
bold: true,
|
|
2552
|
+
children: " Trends · user signups (monthly)"
|
|
2553
|
+
}),
|
|
2554
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2555
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2556
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2557
|
+
color: "gray",
|
|
2558
|
+
children: " 10k ┤"
|
|
2559
|
+
}),
|
|
2560
|
+
" ",
|
|
2561
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2562
|
+
color: "cyan",
|
|
2563
|
+
children: "╭──"
|
|
2564
|
+
}),
|
|
2565
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2566
|
+
dimColor: true,
|
|
2567
|
+
children: " 9,575"
|
|
2568
|
+
})
|
|
2569
|
+
] }),
|
|
2570
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2571
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2572
|
+
color: "gray",
|
|
2573
|
+
children: " │"
|
|
2574
|
+
}),
|
|
2575
|
+
" ",
|
|
2576
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2577
|
+
color: "cyan",
|
|
2578
|
+
children: "╭╯"
|
|
2579
|
+
})
|
|
2580
|
+
] }),
|
|
2581
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2582
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2583
|
+
color: "gray",
|
|
2584
|
+
children: " 7.5k ┤"
|
|
2585
|
+
}),
|
|
2586
|
+
" ",
|
|
2587
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2588
|
+
color: "cyan",
|
|
2589
|
+
children: "╭╯"
|
|
2590
|
+
})
|
|
2591
|
+
] }),
|
|
2592
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2593
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2594
|
+
color: "gray",
|
|
2595
|
+
children: " │"
|
|
2596
|
+
}),
|
|
2597
|
+
" ",
|
|
2598
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2599
|
+
color: "cyan",
|
|
2600
|
+
children: "╭─╯"
|
|
2601
|
+
})
|
|
2602
|
+
] }),
|
|
2603
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2604
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2605
|
+
color: "gray",
|
|
2606
|
+
children: " 5k ┤"
|
|
2607
|
+
}),
|
|
2608
|
+
" ",
|
|
2609
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2610
|
+
color: "cyan",
|
|
2611
|
+
children: "╭─╯"
|
|
2612
|
+
})
|
|
2613
|
+
] }),
|
|
2614
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2615
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2616
|
+
color: "gray",
|
|
2617
|
+
children: " │"
|
|
2618
|
+
}),
|
|
2619
|
+
" ",
|
|
2620
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2621
|
+
color: "cyan",
|
|
2622
|
+
children: "╭──╯"
|
|
2623
|
+
})
|
|
2624
|
+
] }),
|
|
2625
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2626
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2627
|
+
color: "gray",
|
|
2628
|
+
children: " 2.5k ┤"
|
|
2629
|
+
}),
|
|
2630
|
+
" ",
|
|
2631
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2632
|
+
color: "cyan",
|
|
2633
|
+
children: "╭───╯"
|
|
2634
|
+
})
|
|
2635
|
+
] }),
|
|
2636
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2637
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2638
|
+
color: "gray",
|
|
2639
|
+
children: " │"
|
|
2640
|
+
}),
|
|
2641
|
+
" ",
|
|
2642
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2643
|
+
color: "cyan",
|
|
2644
|
+
children: "╭──────╯"
|
|
2645
|
+
})
|
|
2646
|
+
] }),
|
|
2647
|
+
/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2648
|
+
color: "gray",
|
|
2649
|
+
children: " 0 ┤"
|
|
2650
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2651
|
+
color: "cyan",
|
|
2652
|
+
children: "──────╯"
|
|
2653
|
+
})] }),
|
|
2654
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2655
|
+
color: "gray",
|
|
2656
|
+
children: " └┬─────┬─────┬─────┬─────┬──"
|
|
2657
|
+
}),
|
|
2658
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2659
|
+
dimColor: true,
|
|
2660
|
+
children: " May Aug Nov Feb May"
|
|
2661
|
+
})
|
|
2662
|
+
]
|
|
2663
|
+
};
|
|
2664
|
+
const FUNNEL_BLOCK = {
|
|
2665
|
+
type: "lines",
|
|
2666
|
+
interval: 200,
|
|
2667
|
+
pause: 8e3,
|
|
2668
|
+
lines: [
|
|
2669
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2670
|
+
bold: true,
|
|
2671
|
+
children: " Funnel · ride conversion"
|
|
2672
|
+
}),
|
|
2673
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2674
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2675
|
+
" ",
|
|
2676
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2677
|
+
bold: true,
|
|
2678
|
+
children: "1"
|
|
2679
|
+
}),
|
|
2680
|
+
" app_launched",
|
|
2681
|
+
" ",
|
|
2682
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2683
|
+
bold: true,
|
|
2684
|
+
color: "green",
|
|
2685
|
+
children: "100.00%"
|
|
2686
|
+
})
|
|
2687
|
+
] }),
|
|
2688
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2689
|
+
color: "cyan",
|
|
2690
|
+
children: " ██████████████████████████████"
|
|
2691
|
+
}),
|
|
2692
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2693
|
+
dimColor: true,
|
|
2694
|
+
children: " → 1,200 users"
|
|
2695
|
+
}),
|
|
2696
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2697
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2698
|
+
" ",
|
|
2699
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2700
|
+
bold: true,
|
|
2701
|
+
children: "2"
|
|
2702
|
+
}),
|
|
2703
|
+
" ride_requested",
|
|
2704
|
+
" ",
|
|
2705
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2706
|
+
dimColor: true,
|
|
2707
|
+
children: "avg 2m 30s"
|
|
2708
|
+
}),
|
|
2709
|
+
" ",
|
|
2710
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2711
|
+
bold: true,
|
|
2712
|
+
color: "green",
|
|
2713
|
+
children: "72.00%"
|
|
2714
|
+
})
|
|
2715
|
+
] }),
|
|
2716
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2717
|
+
" ",
|
|
2718
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2719
|
+
color: "cyan",
|
|
2720
|
+
children: "██████████████████████"
|
|
2721
|
+
}),
|
|
2722
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2723
|
+
dimColor: true,
|
|
2724
|
+
children: "░░░░░░░░░"
|
|
2725
|
+
})
|
|
2726
|
+
] }),
|
|
2727
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2728
|
+
" ",
|
|
2729
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2730
|
+
dimColor: true,
|
|
2731
|
+
children: "→ 864 users"
|
|
2732
|
+
}),
|
|
2733
|
+
" ",
|
|
2734
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2735
|
+
color: "red",
|
|
2736
|
+
children: "↘"
|
|
2737
|
+
}),
|
|
2738
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2739
|
+
dimColor: true,
|
|
2740
|
+
children: " 336 (28%)"
|
|
2741
|
+
})
|
|
2742
|
+
] }),
|
|
2743
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2744
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2745
|
+
" ",
|
|
2746
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2747
|
+
bold: true,
|
|
2748
|
+
children: "3"
|
|
2749
|
+
}),
|
|
2750
|
+
" ride_accepted",
|
|
2751
|
+
" ",
|
|
2752
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2753
|
+
dimColor: true,
|
|
2754
|
+
children: "avg 5m 12s"
|
|
2755
|
+
}),
|
|
2756
|
+
" ",
|
|
2757
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2758
|
+
bold: true,
|
|
2759
|
+
color: "green",
|
|
2760
|
+
children: "51.00%"
|
|
2761
|
+
})
|
|
2762
|
+
] }),
|
|
2763
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2764
|
+
" ",
|
|
2765
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2766
|
+
color: "cyan",
|
|
2767
|
+
children: "██████████████████"
|
|
2768
|
+
}),
|
|
2769
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2770
|
+
dimColor: true,
|
|
2771
|
+
children: "░░░░░░░░░░░░░"
|
|
2772
|
+
})
|
|
2773
|
+
] }),
|
|
2774
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2775
|
+
" ",
|
|
2776
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2777
|
+
dimColor: true,
|
|
2778
|
+
children: "→ 612 users"
|
|
2779
|
+
}),
|
|
2780
|
+
" ",
|
|
2781
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2782
|
+
color: "red",
|
|
2783
|
+
children: "↘"
|
|
2784
|
+
}),
|
|
2785
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2786
|
+
dimColor: true,
|
|
2787
|
+
children: " 252 (29%)"
|
|
2788
|
+
})
|
|
2789
|
+
] }),
|
|
2790
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2791
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2792
|
+
" ",
|
|
2793
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2794
|
+
bold: true,
|
|
2795
|
+
children: "4"
|
|
2796
|
+
}),
|
|
2797
|
+
" ride_started",
|
|
2798
|
+
" ",
|
|
2799
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2800
|
+
dimColor: true,
|
|
2801
|
+
children: "avg 1m 45s"
|
|
2802
|
+
}),
|
|
2803
|
+
" ",
|
|
2804
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2805
|
+
bold: true,
|
|
2806
|
+
color: "green",
|
|
2807
|
+
children: "38.00%"
|
|
2808
|
+
})
|
|
2809
|
+
] }),
|
|
2810
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2811
|
+
" ",
|
|
2812
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2813
|
+
color: "cyan",
|
|
2814
|
+
children: "█████████████"
|
|
2815
|
+
}),
|
|
2816
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2817
|
+
dimColor: true,
|
|
2818
|
+
children: "░░░░░░░░░░░░░░░░░░"
|
|
2819
|
+
})
|
|
2820
|
+
] }),
|
|
2821
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2822
|
+
" ",
|
|
2823
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2824
|
+
dimColor: true,
|
|
2825
|
+
children: "→ 456 users"
|
|
2826
|
+
}),
|
|
2827
|
+
" ",
|
|
2828
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2829
|
+
color: "red",
|
|
2830
|
+
children: "↘"
|
|
2831
|
+
}),
|
|
2832
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2833
|
+
dimColor: true,
|
|
2834
|
+
children: " 156 (25%)"
|
|
2835
|
+
})
|
|
2836
|
+
] })
|
|
2837
|
+
]
|
|
2838
|
+
};
|
|
2839
|
+
/** Fixed chrome: ScreenContainer (3) + TabContainer tab bar (2) */
|
|
2840
|
+
const FIXED_CHROME = 5;
|
|
2841
|
+
const HEADER_ROWS = 2;
|
|
2842
|
+
const MIN_CONTENT_ROWS = 6;
|
|
2843
|
+
const LearnCard = ({ store, onComplete }) => {
|
|
2844
|
+
const peekedRef = useRef(false);
|
|
2845
|
+
const [columns, rows] = useStdoutDimensions();
|
|
2846
|
+
const blocks = useMemo(() => [
|
|
2847
|
+
{
|
|
2848
|
+
content: "Welcome.",
|
|
2849
|
+
pause: 3e3,
|
|
2850
|
+
mode: 0,
|
|
2851
|
+
animationInterval: 160
|
|
2852
|
+
},
|
|
2853
|
+
{
|
|
2854
|
+
content: "The Wizard is an agent.",
|
|
2855
|
+
pause: 4e3
|
|
2856
|
+
},
|
|
2857
|
+
{
|
|
2858
|
+
content: "It handles the entire PostHog setup process on your behalf.",
|
|
2859
|
+
pause: 5e3
|
|
2860
|
+
},
|
|
2861
|
+
{
|
|
2862
|
+
content: "As we speak, it's building a plan to set up PostHog in your project.",
|
|
2863
|
+
pause: 6e3
|
|
2864
|
+
},
|
|
2865
|
+
{
|
|
2866
|
+
type: "clear",
|
|
2867
|
+
pause: 2e3
|
|
2868
|
+
},
|
|
2869
|
+
{
|
|
2870
|
+
pause: 5e3,
|
|
2871
|
+
persist: true,
|
|
2872
|
+
content: /* @__PURE__ */ jsx(StatusPeekTrigger, {
|
|
2873
|
+
store,
|
|
2874
|
+
peekedRef
|
|
2875
|
+
})
|
|
2876
|
+
},
|
|
2877
|
+
{
|
|
2878
|
+
pause: 6e3,
|
|
2879
|
+
content: /* @__PURE__ */ jsxs(Text, { children: [
|
|
2880
|
+
"Press",
|
|
2881
|
+
" ",
|
|
2882
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2883
|
+
color: Colors.accent,
|
|
2884
|
+
bold: true,
|
|
2885
|
+
children: "S"
|
|
2886
|
+
}),
|
|
2887
|
+
" ",
|
|
2888
|
+
"to expand or collapse the status."
|
|
2889
|
+
] })
|
|
2890
|
+
},
|
|
2891
|
+
{
|
|
2892
|
+
type: "clear",
|
|
2893
|
+
pause: 2e3
|
|
2894
|
+
},
|
|
2895
|
+
{
|
|
2896
|
+
content: "It takes about eight minutes.",
|
|
2897
|
+
pause: 2e3
|
|
2898
|
+
},
|
|
2899
|
+
{
|
|
2900
|
+
content: "So grab some coffee ☕️.",
|
|
2901
|
+
pause: 2e3
|
|
2902
|
+
},
|
|
2903
|
+
{
|
|
2904
|
+
content: "Or stick around and learn about PostHog.",
|
|
2905
|
+
pause: 5e3
|
|
2906
|
+
},
|
|
2907
|
+
{
|
|
2908
|
+
type: "clear",
|
|
2909
|
+
pause: 3e3
|
|
2910
|
+
},
|
|
2911
|
+
{
|
|
2912
|
+
content: "Events are the foundation of the PostHog platform.",
|
|
2913
|
+
pause: 4e3
|
|
2914
|
+
},
|
|
2915
|
+
{
|
|
2916
|
+
content: "Every time an action is performed in your codebase — like button clicks, function calls, or thrown errors — we can capture an event.",
|
|
2917
|
+
pause: 6e3
|
|
2918
|
+
},
|
|
2919
|
+
{
|
|
2920
|
+
content: "Events are sent to PostHog and joined with other product data.",
|
|
2921
|
+
pause: 6e3
|
|
2922
|
+
},
|
|
2923
|
+
{
|
|
2924
|
+
type: "clear",
|
|
2925
|
+
pause: 1e3
|
|
2926
|
+
},
|
|
2927
|
+
{
|
|
2928
|
+
content: "Here's the flow.",
|
|
2929
|
+
pause: 1e3
|
|
2930
|
+
},
|
|
2931
|
+
POSTHOG_DATA_FLOW,
|
|
2932
|
+
{
|
|
2933
|
+
type: "clear",
|
|
2934
|
+
pause: 2e3
|
|
2935
|
+
},
|
|
2936
|
+
{
|
|
2937
|
+
content: "With enough event data, you can answer powerful questions about your product.",
|
|
2938
|
+
pause: 4e3
|
|
2939
|
+
},
|
|
2940
|
+
{
|
|
2941
|
+
content: "And create insights.",
|
|
2942
|
+
pause: 4e3
|
|
2943
|
+
},
|
|
2944
|
+
{
|
|
2945
|
+
type: "clear",
|
|
2946
|
+
pause: 500
|
|
2947
|
+
},
|
|
2948
|
+
{
|
|
2949
|
+
content: "Like trends to measure growth.",
|
|
2950
|
+
pause: 2500
|
|
2951
|
+
},
|
|
2952
|
+
LINE_CHART_BLOCK,
|
|
2953
|
+
{
|
|
2954
|
+
type: "clear",
|
|
2955
|
+
pause: 500
|
|
2956
|
+
},
|
|
2957
|
+
{
|
|
2958
|
+
content: "Or funnels to reveal bottlenecks.",
|
|
2959
|
+
pause: 2500
|
|
2960
|
+
},
|
|
2961
|
+
FUNNEL_BLOCK,
|
|
2962
|
+
{
|
|
2963
|
+
type: "clear",
|
|
2964
|
+
pause: 1e3
|
|
2965
|
+
},
|
|
2966
|
+
{
|
|
2967
|
+
content: "Use those signals to decide what to build next.",
|
|
2968
|
+
pause: 4e3
|
|
2969
|
+
},
|
|
2970
|
+
{
|
|
2971
|
+
content: "PostHog has all the dev tools you need.",
|
|
2972
|
+
pause: 3e3
|
|
2973
|
+
},
|
|
2974
|
+
PRODUCT_SUITE_BLOCK
|
|
2975
|
+
], [store]);
|
|
2976
|
+
const statusBarRows = (store ? store.statusMessages.length > 0 : false) ? (store?.statusExpanded ? 10 : 2) + 1 : 0;
|
|
2977
|
+
const contentHeight = rows - FIXED_CHROME - statusBarRows;
|
|
2978
|
+
const tooSmall = contentHeight < MIN_CONTENT_ROWS;
|
|
2979
|
+
const maxHeight = Math.max(1, contentHeight - HEADER_ROWS);
|
|
2980
|
+
const paneWidth = Math.floor((Math.min(120, columns) - 2) / 2) - 2;
|
|
2981
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2982
|
+
flexDirection: "column",
|
|
2983
|
+
paddingX: 1,
|
|
2984
|
+
display: tooSmall ? "none" : "flex",
|
|
2985
|
+
children: [
|
|
2986
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2987
|
+
bold: true,
|
|
2988
|
+
color: Colors.accent,
|
|
2989
|
+
children: "Learn"
|
|
2990
|
+
}),
|
|
2991
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
2992
|
+
/* @__PURE__ */ jsx(ContentSequencer, {
|
|
2993
|
+
blocks,
|
|
2994
|
+
mode: 2,
|
|
2995
|
+
maxHeight,
|
|
2996
|
+
availableWidth: paneWidth,
|
|
2997
|
+
startDelay: 2e3,
|
|
2998
|
+
initialBlockIdx: store?.learnCardBlockIdx ?? 0,
|
|
2999
|
+
onBlockChange: (idx) => store?.setLearnCardBlockIdx(idx),
|
|
3000
|
+
onSequenceComplete: onComplete
|
|
3001
|
+
})
|
|
3002
|
+
]
|
|
3003
|
+
});
|
|
3004
|
+
};
|
|
3005
|
+
//#endregion
|
|
3006
|
+
//#region src/ui/tui/components/TipsCard.tsx
|
|
3007
|
+
/**
|
|
3008
|
+
* TipsCard — Shows PostHog tips during the agent run.
|
|
3009
|
+
* Reactively shows/hides tips based on discovered features.
|
|
3010
|
+
* Supports toggling additional features via key bindings.
|
|
3011
|
+
*/
|
|
3012
|
+
const TIPS = [
|
|
3013
|
+
{
|
|
3014
|
+
id: "persons",
|
|
3015
|
+
title: "You can also track people and groups with PostHog",
|
|
3016
|
+
description: "Events can be associated with the humans who generate them, letting you understand a specific user or customer's situation."
|
|
3017
|
+
},
|
|
3018
|
+
{
|
|
3019
|
+
id: "properties",
|
|
3020
|
+
title: "Get way more detail using properties",
|
|
3021
|
+
description: "Events and person records can have any properties you want. Track things like how they found your website, what subscription tier they choose, and much more."
|
|
3022
|
+
},
|
|
3023
|
+
{
|
|
3024
|
+
id: "stripe",
|
|
3025
|
+
title: "You can track Stripe revenue with PostHog",
|
|
3026
|
+
description: "Add Stripe as a data source while you wait:",
|
|
3027
|
+
url: "https://app.posthog.com/project/data-warehouse/new-source?kind=Stripe",
|
|
3028
|
+
visible: (store) => store.session.discoveredFeatures.includes("stripe")
|
|
3029
|
+
},
|
|
3030
|
+
{
|
|
3031
|
+
id: "llm",
|
|
3032
|
+
title: "PostHog can also help you track your LLM costs",
|
|
3033
|
+
description: "",
|
|
3034
|
+
visible: (store) => store.session.discoveredFeatures.includes("llm"),
|
|
3035
|
+
toggle: {
|
|
3036
|
+
key: "l",
|
|
3037
|
+
feature: "llm",
|
|
3038
|
+
enabledLabel: "LLM analytics setup queued next",
|
|
3039
|
+
prompt: "We detected LLM dependencies in your project.",
|
|
3040
|
+
isEnabled: (store) => store.session.llmOptIn
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
];
|
|
3044
|
+
const TipsCard = ({ store }) => {
|
|
3045
|
+
useInput((input) => {
|
|
3046
|
+
for (const tip of TIPS) if (tip.toggle && input.toLowerCase() === tip.toggle.key && (!tip.visible || tip.visible(store)) && !tip.toggle.isEnabled(store)) store.enableFeature(tip.toggle.feature);
|
|
3047
|
+
});
|
|
3048
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
3049
|
+
flexDirection: "column",
|
|
3050
|
+
paddingX: 1,
|
|
3051
|
+
children: [
|
|
3052
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3053
|
+
bold: true,
|
|
3054
|
+
color: Colors.accent,
|
|
3055
|
+
children: "Tips"
|
|
3056
|
+
}),
|
|
3057
|
+
/* @__PURE__ */ jsx(Box, { height: 1 }),
|
|
3058
|
+
TIPS.filter((tip) => !tip.visible || tip.visible(store)).map((tip) => /* @__PURE__ */ jsxs(Box, {
|
|
3059
|
+
flexDirection: "column",
|
|
3060
|
+
marginBottom: 1,
|
|
3061
|
+
children: [/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
3062
|
+
color: Colors.accent,
|
|
3063
|
+
children: [Icons.diamond, " "]
|
|
3064
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
3065
|
+
bold: true,
|
|
3066
|
+
children: tip.title
|
|
3067
|
+
})] }), tip.toggle ? tip.toggle.isEnabled(store) ? /* @__PURE__ */ jsxs(Text, {
|
|
3068
|
+
color: Colors.success,
|
|
3069
|
+
children: [
|
|
3070
|
+
Icons.check,
|
|
3071
|
+
" ",
|
|
3072
|
+
tip.toggle.enabledLabel
|
|
3073
|
+
]
|
|
3074
|
+
}) : /* @__PURE__ */ jsxs(Text, {
|
|
3075
|
+
dimColor: true,
|
|
3076
|
+
children: [
|
|
3077
|
+
tip.toggle.prompt,
|
|
3078
|
+
" Press",
|
|
3079
|
+
" ",
|
|
3080
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3081
|
+
bold: true,
|
|
3082
|
+
color: Colors.accent,
|
|
3083
|
+
children: tip.toggle.key.toUpperCase()
|
|
3084
|
+
}),
|
|
3085
|
+
" ",
|
|
3086
|
+
"to enable."
|
|
3087
|
+
]
|
|
3088
|
+
}) : /* @__PURE__ */ jsxs(Text, {
|
|
3089
|
+
dimColor: true,
|
|
3090
|
+
children: [tip.description, tip.url && /* @__PURE__ */ jsxs(Fragment, { children: [" ", /* @__PURE__ */ jsx(Text, {
|
|
3091
|
+
color: "cyan",
|
|
3092
|
+
children: tip.url
|
|
3093
|
+
})] })]
|
|
3094
|
+
})]
|
|
3095
|
+
}, tip.id))
|
|
3096
|
+
]
|
|
3097
|
+
});
|
|
3098
|
+
};
|
|
3099
|
+
//#endregion
|
|
3100
|
+
//#region src/ui/tui/components/ServiceHealthList.tsx
|
|
3101
|
+
/**
|
|
3102
|
+
* ServiceHealthList — Shared component for displaying service health status.
|
|
3103
|
+
*
|
|
3104
|
+
* Used by HealthCheckScreen (blocking services only) and HealthWarningsTab (all services).
|
|
3105
|
+
*/
|
|
3106
|
+
/** Keys that are component-level detail — shown inline under their parent. */
|
|
3107
|
+
const COMPONENT_KEYS = [
|
|
3108
|
+
"posthogComponents",
|
|
3109
|
+
"npmComponents",
|
|
3110
|
+
"cloudflareComponents"
|
|
3111
|
+
];
|
|
3112
|
+
/** Map component key → its parent "overall" key */
|
|
3113
|
+
const COMPONENT_PARENT = {
|
|
3114
|
+
posthogComponents: "posthogOverall",
|
|
3115
|
+
npmComponents: "npmOverall",
|
|
3116
|
+
cloudflareComponents: "cloudflareOverall"
|
|
3117
|
+
};
|
|
3118
|
+
function statusIcon(status) {
|
|
3119
|
+
switch (status) {
|
|
3120
|
+
case "down": return {
|
|
3121
|
+
icon: Icons.squareFilled,
|
|
3122
|
+
color: "red"
|
|
3123
|
+
};
|
|
3124
|
+
case "degraded": return {
|
|
3125
|
+
icon: Icons.squareFilled,
|
|
3126
|
+
color: "#DC9300"
|
|
3127
|
+
};
|
|
3128
|
+
case "healthy": return {
|
|
3129
|
+
icon: Icons.check,
|
|
3130
|
+
color: "green"
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
const ServiceHealthList = ({ health, filterKeys, showHealthy = true }) => {
|
|
3135
|
+
const topLevelKeys = Object.keys(health).filter((k) => !COMPONENT_KEYS.includes(k));
|
|
3136
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
3137
|
+
flexDirection: "column",
|
|
3138
|
+
paddingLeft: 1,
|
|
3139
|
+
children: (filterKeys ? topLevelKeys.filter((k) => filterKeys.includes(k)) : topLevelKeys).map((key) => {
|
|
3140
|
+
const result = health[key];
|
|
3141
|
+
if (!showHealthy && result.status === "healthy") return null;
|
|
3142
|
+
const { icon, color } = statusIcon(result.status);
|
|
3143
|
+
const label = SERVICE_LABELS[key];
|
|
3144
|
+
const componentKey = Object.entries(COMPONENT_PARENT).find(([, parent]) => parent === key)?.[0];
|
|
3145
|
+
const affectedComponents = (componentKey ? health[componentKey] : void 0)?.degradedOrDownComponents ?? [];
|
|
3146
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
3147
|
+
flexDirection: "column",
|
|
3148
|
+
children: [/* @__PURE__ */ jsxs(Text, { children: [
|
|
3149
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3150
|
+
color,
|
|
3151
|
+
children: icon
|
|
3152
|
+
}),
|
|
3153
|
+
" ",
|
|
3154
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3155
|
+
bold: result.status !== "healthy",
|
|
3156
|
+
children: label
|
|
3157
|
+
})
|
|
3158
|
+
] }), affectedComponents.length > 0 && /* @__PURE__ */ jsxs(Box, {
|
|
3159
|
+
flexDirection: "column",
|
|
3160
|
+
paddingLeft: 3,
|
|
3161
|
+
children: [affectedComponents.slice(0, 5).map((c) => {
|
|
3162
|
+
const ci = statusIcon(c.status);
|
|
3163
|
+
return /* @__PURE__ */ jsxs(Text, {
|
|
3164
|
+
dimColor: true,
|
|
3165
|
+
children: [
|
|
3166
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3167
|
+
color: ci.color,
|
|
3168
|
+
children: ci.icon
|
|
3169
|
+
}),
|
|
3170
|
+
" ",
|
|
3171
|
+
c.name
|
|
3172
|
+
]
|
|
3173
|
+
}, c.name);
|
|
3174
|
+
}), affectedComponents.length > 5 && /* @__PURE__ */ jsxs(Text, {
|
|
3175
|
+
dimColor: true,
|
|
3176
|
+
children: [
|
|
3177
|
+
"+",
|
|
3178
|
+
affectedComponents.length - 5,
|
|
3179
|
+
" more"
|
|
3180
|
+
]
|
|
3181
|
+
})]
|
|
3182
|
+
})]
|
|
3183
|
+
}, key);
|
|
3184
|
+
})
|
|
3185
|
+
});
|
|
3186
|
+
};
|
|
3187
|
+
//#endregion
|
|
3188
|
+
//#region src/ui/tui/screens/McpScreen.tsx
|
|
3189
|
+
/**
|
|
3190
|
+
* McpScreen — MCP server install/remove flow.
|
|
3191
|
+
*
|
|
3192
|
+
* Uses an McpInstaller service (passed via props) instead of
|
|
3193
|
+
* importing business logic directly. Testable, no dynamic imports.
|
|
3194
|
+
*
|
|
3195
|
+
* Supports two modes via the `mode` prop:
|
|
3196
|
+
* - 'install': detect clients → confirm → [pick clients] → pick features → install
|
|
3197
|
+
* - 'remove': detect installed clients → confirm → remove
|
|
3198
|
+
*
|
|
3199
|
+
* When done, calls store.setMcpComplete(). The router resolves to outro.
|
|
3200
|
+
*/
|
|
3201
|
+
const markDone = (store, outcome, clients = [], standalone = false) => {
|
|
3202
|
+
store.setMcpComplete(outcome, clients);
|
|
3203
|
+
if (standalone) process.exit(0);
|
|
3204
|
+
};
|
|
3205
|
+
const McpScreen = ({ store, installer, mode = "install", standalone = false }) => {
|
|
3206
|
+
useSyncExternalStore((cb) => store.subscribe(cb), () => store.getSnapshot());
|
|
3207
|
+
useInput(() => void 0);
|
|
3208
|
+
const isRemove = mode === "remove";
|
|
3209
|
+
const [phase, setPhase] = useState("detecting");
|
|
3210
|
+
const [clients, setClients] = useState([]);
|
|
3211
|
+
const [selectedClientNames, setSelectedClientNames] = useState([]);
|
|
3212
|
+
const [resultClients, setResultClients] = useState([]);
|
|
3213
|
+
useEffect(() => {
|
|
3214
|
+
(async () => {
|
|
3215
|
+
try {
|
|
3216
|
+
const detected = await installer.detectClients();
|
|
3217
|
+
if (detected.length === 0) {
|
|
3218
|
+
setPhase("none");
|
|
3219
|
+
setTimeout(() => markDone(store, "no_clients", [], standalone), 1500);
|
|
3220
|
+
} else {
|
|
3221
|
+
setClients(detected);
|
|
3222
|
+
setPhase("ask");
|
|
3223
|
+
}
|
|
3224
|
+
} catch {
|
|
3225
|
+
setPhase("none");
|
|
3226
|
+
setTimeout(() => markDone(store, "failed", [], standalone), 1500);
|
|
3227
|
+
}
|
|
3228
|
+
})();
|
|
3229
|
+
}, [installer]);
|
|
3230
|
+
const proceedToFeatureSelectOrInstall = (clientNames) => {
|
|
3231
|
+
setSelectedClientNames(clientNames);
|
|
3232
|
+
if (store.session.mcpFeatures) doInstall(clientNames, store.session.mcpFeatures);
|
|
3233
|
+
else setPhase("feature-select");
|
|
3234
|
+
};
|
|
3235
|
+
const handleConfirm = () => {
|
|
3236
|
+
if (isRemove) doRemove();
|
|
3237
|
+
else if (clients.length === 1) proceedToFeatureSelectOrInstall(clients.map((c) => c.name));
|
|
3238
|
+
else setPhase("pick");
|
|
3239
|
+
};
|
|
3240
|
+
const handleSkip = () => {
|
|
3241
|
+
markDone(store, "skipped", [], standalone);
|
|
3242
|
+
};
|
|
3243
|
+
const doInstall = async (names, features) => {
|
|
3244
|
+
setPhase("working");
|
|
3245
|
+
let result = [];
|
|
3246
|
+
try {
|
|
3247
|
+
result = await installer.install(names, features, store.session.apiKey);
|
|
3248
|
+
setResultClients(result);
|
|
3249
|
+
} catch {
|
|
3250
|
+
setResultClients([]);
|
|
3251
|
+
}
|
|
3252
|
+
setPhase("done");
|
|
3253
|
+
const outcome = result.length > 0 ? "installed" : "failed";
|
|
3254
|
+
setTimeout(() => markDone(store, outcome, result, standalone), 2e3);
|
|
3255
|
+
};
|
|
3256
|
+
const doRemove = async () => {
|
|
3257
|
+
setPhase("working");
|
|
3258
|
+
let result = [];
|
|
3259
|
+
try {
|
|
3260
|
+
result = await installer.remove();
|
|
3261
|
+
setResultClients(result);
|
|
3262
|
+
} catch {
|
|
3263
|
+
setResultClients([]);
|
|
3264
|
+
}
|
|
3265
|
+
setPhase("done");
|
|
3266
|
+
const outcome = result.length > 0 ? "installed" : "failed";
|
|
3267
|
+
setTimeout(() => markDone(store, outcome, result, standalone), 2e3);
|
|
3268
|
+
};
|
|
3269
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
3270
|
+
flexDirection: "column",
|
|
3271
|
+
flexGrow: 1,
|
|
3272
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
3273
|
+
bold: true,
|
|
3274
|
+
color: Colors.accent,
|
|
3275
|
+
children: ["MCP Server ", isRemove ? "Removal" : "Setup"]
|
|
3276
|
+
}), /* @__PURE__ */ jsxs(Box, {
|
|
3277
|
+
marginTop: 1,
|
|
3278
|
+
flexDirection: "column",
|
|
3279
|
+
children: [
|
|
3280
|
+
phase === "detecting" && /* @__PURE__ */ jsx(Text, {
|
|
3281
|
+
dimColor: true,
|
|
3282
|
+
children: "Detecting supported editors..."
|
|
3283
|
+
}),
|
|
3284
|
+
phase === "none" && /* @__PURE__ */ jsxs(Text, {
|
|
3285
|
+
dimColor: true,
|
|
3286
|
+
children: [
|
|
3287
|
+
"No ",
|
|
3288
|
+
isRemove ? "installed" : "supported",
|
|
3289
|
+
" MCP clients detected. Skipping..."
|
|
3290
|
+
]
|
|
3291
|
+
}),
|
|
3292
|
+
phase === "ask" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
3293
|
+
dimColor: true,
|
|
3294
|
+
children: ["Detected: ", clients.map((c) => c.name).join(", ")]
|
|
3295
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
3296
|
+
marginTop: 1,
|
|
3297
|
+
children: /* @__PURE__ */ jsx(ConfirmationInput, {
|
|
3298
|
+
message: isRemove ? "Remove the PostHog MCP server from your editor?" : "Install the PostHog MCP server to your editor?",
|
|
3299
|
+
confirmLabel: isRemove ? "Remove MCP" : "Install MCP",
|
|
3300
|
+
cancelLabel: "No thanks",
|
|
3301
|
+
onConfirm: handleConfirm,
|
|
3302
|
+
onCancel: handleSkip
|
|
3303
|
+
})
|
|
3304
|
+
})] }),
|
|
3305
|
+
phase === "pick" && /* @__PURE__ */ jsx(PickerMenu, {
|
|
3306
|
+
message: "Select editor to install MCP server",
|
|
3307
|
+
options: clients.map((c) => ({
|
|
3308
|
+
label: c.name,
|
|
3309
|
+
value: c.name
|
|
3310
|
+
})),
|
|
3311
|
+
mode: "multi",
|
|
3312
|
+
onSelect: (selected) => {
|
|
3313
|
+
proceedToFeatureSelectOrInstall(Array.isArray(selected) ? selected : [selected]);
|
|
3314
|
+
}
|
|
3315
|
+
}),
|
|
3316
|
+
phase === "feature-select" && /* @__PURE__ */ jsx(GroupedPickerMenu, {
|
|
3317
|
+
message: "Select features to enable",
|
|
3318
|
+
groups: AVAILABLE_FEATURES,
|
|
3319
|
+
initialSelected: [...ALL_FEATURE_VALUES],
|
|
3320
|
+
onSelect: (features) => {
|
|
3321
|
+
doInstall(selectedClientNames, features);
|
|
3322
|
+
}
|
|
3323
|
+
}),
|
|
3324
|
+
phase === "working" && /* @__PURE__ */ jsxs(Text, {
|
|
3325
|
+
dimColor: true,
|
|
3326
|
+
children: [isRemove ? "Removing" : "Installing", " MCP server..."]
|
|
3327
|
+
}),
|
|
3328
|
+
phase === "done" && /* @__PURE__ */ jsx(Box, {
|
|
3329
|
+
flexDirection: "column",
|
|
3330
|
+
children: resultClients.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Text, {
|
|
3331
|
+
color: "green",
|
|
3332
|
+
bold: true,
|
|
3333
|
+
children: [
|
|
3334
|
+
"✔",
|
|
3335
|
+
" MCP server",
|
|
3336
|
+
" ",
|
|
3337
|
+
isRemove ? "removed from" : "installed for",
|
|
3338
|
+
":"
|
|
3339
|
+
]
|
|
3340
|
+
}), resultClients.map((name, i) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
3341
|
+
" ",
|
|
3342
|
+
"•",
|
|
3343
|
+
" ",
|
|
3344
|
+
name
|
|
3345
|
+
] }, i))] }) : /* @__PURE__ */ jsxs(Text, {
|
|
3346
|
+
dimColor: true,
|
|
3347
|
+
children: [isRemove ? "Removal" : "Installation", " skipped."]
|
|
3348
|
+
})
|
|
3349
|
+
})
|
|
3350
|
+
]
|
|
3351
|
+
})]
|
|
3352
|
+
});
|
|
3353
|
+
};
|
|
3354
|
+
//#endregion
|
|
3355
|
+
export { SplitView as _, HNViewer as a, Icons as b, EventPlanViewer as c, ConfirmationInput as d, GroupedPickerMenu as f, LoadingBox as g, ProgressList as h, LearnCard as i, LogViewer as l, PickerMenu as m, ServiceHealthList as n, TabContainer as o, useStdoutDimensions as p, TipsCard as r, ScreenContainer as s, McpScreen as t, ModalOverlay as u, CardLayout as v, WizardStore as x, Colors as y };
|
|
3356
|
+
|
|
3357
|
+
//# sourceMappingURL=McpScreen-BmHapIaP.js.map
|