mixdog 1.4.1 → 1.4.2
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/package.json +3 -2
- package/scripts/build-shipped-history.mjs +115 -33
- package/scripts/gate-local.mjs +172 -61
- package/scripts/lib/gate-scheduler.mjs +138 -32
- package/scripts/lib/gate-timing-reporter.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +54 -29
- package/scripts/lib/scoped-temp.mjs +1 -0
- package/scripts/lib/test-failure-records.mjs +14 -1
- package/scripts/lib/test-failure-summary.mjs +16 -4
- package/scripts/lib/test-spawn-ledger.mjs +12 -3
- package/scripts/pptx-kit-index.mjs +5 -6
- package/scripts/suite-health.mjs +110 -28
- package/scripts/test.mjs +9 -14
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/explore/AGENT.md +22 -0
- package/src/agents/explore/agent.json +6 -0
- package/src/cli.mjs +7 -0
- package/src/defaults/agents.json +7 -0
- package/src/defaults/shipped-history.json +18 -2
- package/src/defaults/skills/browser-use/SKILL.md +4 -1
- package/src/defaults/skills/code-tidy/SKILL.md +71 -102
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
- package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
- package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
- package/src/defaults/skills/computer-use/SKILL.md +12 -2
- package/src/defaults/skills/goal-management/SKILL.md +1 -1
- package/src/defaults/skills/setup/references/actions.md +12 -6
- package/src/defaults/skills/setup/references/surfaces.md +2 -2
- package/src/defaults/skills/setup-guide/SKILL.md +5 -2
- package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
- package/src/defaults/skills/skill-creator/SKILL.md +85 -190
- package/src/defaults/skills/skill-creator/references/design.md +70 -162
- package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
- package/src/defaults/skills/test-suite/SKILL.md +194 -0
- package/src/defaults/skills/test-suite/references/case-management.md +51 -0
- package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
- package/src/defaults/skills/test-suite/references/reliability.md +49 -0
- package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
- package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
- package/src/defaults/skills/visualize/SKILL.md +6 -2
- package/src/lib/rules-builder.cjs +6 -30
- package/src/rules/lead/LEAD.md +3 -3
- package/src/rules/shared/20-research.md +3 -6
- package/src/rules/shared/40-editing.md +1 -0
- package/src/rules/shared/60-verification.md +10 -9
- package/src/rules/shared/70-delivery.md +4 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +130 -21
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
- package/src/runtime/agent/orchestrator/config.mjs +12 -7
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +7 -7
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
- package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
- package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +83 -52
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +43 -8
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +30 -28
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +48 -31
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +26 -28
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +33 -2
- package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +94 -10
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +10 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +5 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +3 -4
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -14
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +17 -14
- package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +47 -29
- package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +243 -38
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +36 -26
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +3 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/channels/lib/whisper-port.mjs +19 -10
- package/src/runtime/channels/lib/whisper-server.mjs +10 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
- package/src/runtime/connectors/gmail-app-password.mjs +3 -3
- package/src/runtime/explore/explorer.mjs +93 -0
- package/src/runtime/explore/tool-defs.mjs +35 -0
- package/src/runtime/explore/tool.mjs +123 -0
- package/src/runtime/memory/index.mjs +4 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +4 -0
- package/src/runtime/office/quality/assurance-structure-docx.mjs +1 -1
- package/src/runtime/shared/agent-route-config.mjs +17 -2
- package/src/runtime/shared/atomic-file.mjs +103 -2
- package/src/runtime/shared/background-tasks.mjs +15 -6
- package/src/runtime/shared/connector-accounts.mjs +5 -5
- package/src/runtime/shared/connector-catalog.d.mts +11 -4
- package/src/runtime/shared/connector-catalog.mjs +60 -52
- package/src/runtime/shared/custom-provider-keys.mjs +20 -0
- package/src/runtime/shared/file-has-data.mjs +26 -2
- package/src/runtime/shared/file-lock.mjs +13 -15
- package/src/runtime/shared/git-launch.d.mts +9 -0
- package/src/runtime/shared/git-launch.mjs +213 -0
- package/src/runtime/shared/git-runner-worker.mjs +2 -1
- package/src/runtime/shared/llm/cost.mjs +6 -1
- package/src/runtime/shared/llm/http-agent.mjs +313 -29
- package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
- package/src/runtime/shared/llm/model-catalog.mjs +46 -6
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
- package/src/runtime/shared/llm/system-proxy.mjs +452 -0
- package/src/runtime/shared/provider-accounts.mjs +52 -2
- package/src/runtime/shared/staged-install-worker.mjs +5 -0
- package/src/runtime/shared/staged-update.mjs +47 -2
- package/src/runtime/shared/tool-surface.mjs +110 -36
- package/src/runtime/shared/tool-work-units.mjs +4 -0
- package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
- package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
- package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
- package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
- package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
- package/src/runtime/shared/worktrees/git.mjs +57 -0
- package/src/runtime/shared/worktrees/manager.mjs +205 -137
- package/src/runtime/tidy/install.mjs +95 -15
- package/src/runtime/tidy/languages.mjs +17 -15
- package/src/runtime/tidy/process.mjs +7 -4
- package/src/runtime/tidy/resolve.mjs +19 -16
- package/src/runtime/tidy/runners/biome.mjs +3 -3
- package/src/runtime/tidy/tool.mjs +1 -1
- package/src/runtime/tidy/worktree.mjs +10 -6
- package/src/runtime/visualize-bridge/client.mjs +4 -1
- package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
- package/src/runtime/web-search/index.mjs +1 -1
- package/src/session-runtime/agent-task-sessions.mjs +410 -353
- package/src/session-runtime/boot/tools.mjs +2 -0
- package/src/session-runtime/bot-status.mjs +103 -103
- package/src/session-runtime/connector-merge-migration.mjs +18 -15
- package/src/session-runtime/conversation-import/discovery.mjs +159 -40
- package/src/session-runtime/conversation-import/service.mjs +9 -3
- package/src/session-runtime/handoff-attachments.mjs +1 -1
- package/src/session-runtime/held-requests.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +143 -116
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
- package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
- package/src/session-runtime/mixy-heartbeat.mjs +1 -1
- package/src/session-runtime/provider-auth/login.mjs +2 -2
- package/src/session-runtime/resource-mcp-api.mjs +98 -35
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
- package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
- package/src/session-runtime/resource-mcp-servers.mjs +5 -4
- package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
- package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
- package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
- package/src/session-runtime/services/daemon-candidate.mjs +4 -1
- package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
- package/src/session-runtime/services/plugin-admin.mjs +10 -6
- package/src/session-runtime/services/provider-admin.mjs +281 -10
- package/src/session-runtime/session-turn-api.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
- package/src/session-runtime/settings-system-api.mjs +1 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
- package/src/session-runtime/setup-tool/executor.mjs +6 -2
- package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
- package/src/session-runtime/turn/agent-ops.mjs +23 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +10 -1
- package/src/session-runtime/workspace/activity-guards.mjs +176 -0
- package/src/session-runtime/workspace/session-view.mjs +149 -0
- package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
- package/src/session-runtime/workspace/transition-runner.mjs +188 -0
- package/src/session-runtime/workspace/transitions.mjs +108 -1066
- package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
- package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
- package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
- package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +54 -54
- package/src/standalone/daemon-desktop-runtime.mjs +2 -0
- package/src/standalone/daemon-session-control.mjs +151 -136
- package/src/standalone/daemon.mjs +7 -0
- package/src/standalone/desktop-service-registry.mjs +9 -0
- package/src/standalone/local-session-runtime.mjs +5 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
- package/src/standalone/session-client.mjs +5 -1
- package/src/standalone/session-frame-stream.mjs +83 -11
- package/src/standalone/session-protocol.mjs +4 -0
- package/src/standalone/session-service/agent-tree.mjs +45 -9
- package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
- package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
- package/src/standalone/session-service/stored-reader.mjs +127 -40
- package/src/tui/App.jsx +154 -164
- package/src/tui/app/app-view/floating-panel.jsx +3 -0
- package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
- package/src/tui/app/clipboard.mjs +21 -10
- package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
- package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
- package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
- package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
- package/src/tui/app/provider-setup/oauth-actions.mjs +25 -6
- package/src/tui/app/provider-setup-picker.mjs +20 -13
- package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
- package/src/tui/app/slash-dispatch.mjs +1 -1
- package/src/tui/app/use-btw-panel.mjs +29 -0
- package/src/tui/app/use-context-panel-refresh.mjs +31 -0
- package/src/tui/app/use-model-catalog-cache.mjs +13 -10
- package/src/tui/app/use-mouse-input.mjs +11 -0
- package/src/tui/app/use-onboarding-start.mjs +8 -3
- package/src/tui/app/use-panel-transition.mjs +10 -1
- package/src/tui/app/use-pasted-buffers.mjs +28 -20
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
- package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
- package/src/tui/app/use-prompt-queue-history.mjs +3 -2
- package/src/tui/app/use-selection-focus.mjs +11 -2
- package/src/tui/app/use-selection-geometry.mjs +18 -9
- package/src/tui/app/use-selection-paint.mjs +4 -4
- package/src/tui/app/use-selection-stitch.mjs +3 -3
- package/src/tui/app/use-slash-palette.mjs +62 -0
- package/src/tui/app/use-terminal-chrome.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +47 -32
- package/src/tui/app/use-transcript-follow.mjs +12 -4
- package/src/tui/app/use-transcript-scroll.mjs +46 -17
- package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
- package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
- package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
- package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
- package/src/tui/app/use-transcript-window.mjs +21 -11
- package/src/tui/app/use-tui-ready.mjs +18 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
- package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
- package/src/tui/components/Picker.jsx +27 -3
- package/src/tui/components/SignInUrl.jsx +68 -0
- package/src/tui/components/TextEntryPanel.jsx +76 -46
- package/src/tui/components/sign-in-url-layout.mjs +24 -0
- package/src/tui/dist/index.mjs +1896 -1341
- package/src/tui/session/goal-turn-state.mjs +6 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +8 -0
- package/src/tui/session/session-api/intake/abort.mjs +2 -1
- package/src/tui/session/session-api/intake.mjs +2 -0
- package/src/tui/session/session-api/integrations.mjs +2 -2
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +2 -2
- package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
- package/src/tui/session/transcript-spill.mjs +92 -4
- package/src/tui/session/transcript-store.mjs +2 -0
- package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
|
@@ -19,6 +19,8 @@ import { theme } from '../theme.mjs';
|
|
|
19
19
|
import { ConfirmBar, clampConfirmFocus } from './ConfirmBar.jsx';
|
|
20
20
|
import { truncatePanelText as truncateText } from './panel-cell-text.mjs';
|
|
21
21
|
import { PickerItemRow } from './picker/PickerItemRow.jsx';
|
|
22
|
+
import { SignInUrl, isSignInCopyKey, signInUrlNote, useCopyUrl } from './SignInUrl.jsx';
|
|
23
|
+
import { signInUrlLayout } from './sign-in-url-layout.mjs';
|
|
22
24
|
import {
|
|
23
25
|
ADJUST_HELP,
|
|
24
26
|
CONFIRM_HELP,
|
|
@@ -43,6 +45,10 @@ export function Picker({
|
|
|
43
45
|
onHighlight,
|
|
44
46
|
title,
|
|
45
47
|
description = '',
|
|
48
|
+
// Optional sign-in URL, drawn complete and borderless under the panel (never
|
|
49
|
+
// truncated like `description`); Ctrl+Y copies it. `copyText` overrides the clipboard.
|
|
50
|
+
detail = '',
|
|
51
|
+
copyText,
|
|
46
52
|
footer = '',
|
|
47
53
|
help,
|
|
48
54
|
columns = 80,
|
|
@@ -89,7 +95,21 @@ export function Picker({
|
|
|
89
95
|
const footerGap = footerLines.length > 0 ? Math.max(0, Math.floor(Number(footerGapRows) || 0)) : 0;
|
|
90
96
|
const footerReserveRows = footerLines.length > 0 ? footerLines.length + footerGap : 0;
|
|
91
97
|
const confirmReserveRows = hasConfirm && footerLines.length === 0 ? 2 : 0;
|
|
92
|
-
const
|
|
98
|
+
const detailText = String(detail || '').trim();
|
|
99
|
+
// The URL is drawn under the panel (no border). Its rows (wrapped URL + copy
|
|
100
|
+
// hint) come out of the list so the total height is unchanged. It gets the
|
|
101
|
+
// rows left beside up to two list rows (one, if that leaves nothing) and
|
|
102
|
+
// degrades step by step (see sign-in-url-layout.mjs) until it fits.
|
|
103
|
+
const rowsAfterChrome = visibleLimit - footerReserveRows - confirmReserveRows;
|
|
104
|
+
const keptListRows = rowsAfterChrome - Math.min(items.length, 2) >= 1 ? Math.min(items.length, 2) : 1;
|
|
105
|
+
const urlLayout = signInUrlLayout(detailText, columns, rowsAfterChrome - keptListRows);
|
|
106
|
+
const detailRows = urlLayout.rows;
|
|
107
|
+
const copyUrl = useCopyUrl(detailText, copyText);
|
|
108
|
+
const handleKey = (input, key, item, index) => {
|
|
109
|
+
if (detailText && isSignInCopyKey(input, key)) copyUrl.copy();
|
|
110
|
+
else onKey?.(input, key, item, index);
|
|
111
|
+
};
|
|
112
|
+
const effectiveVisibleLimit = Math.max(1, visibleLimit - footerReserveRows - confirmReserveRows - detailRows);
|
|
93
113
|
let helpText = help;
|
|
94
114
|
if (!helpText) {
|
|
95
115
|
if (hasConfirm) helpText = CONFIRM_HELP;
|
|
@@ -112,7 +132,7 @@ export function Picker({
|
|
|
112
132
|
onLeft,
|
|
113
133
|
onRight,
|
|
114
134
|
onTab,
|
|
115
|
-
onKey,
|
|
135
|
+
onKey: handleKey,
|
|
116
136
|
effectiveVisibleLimit,
|
|
117
137
|
hasConfirm,
|
|
118
138
|
confirmButtons,
|
|
@@ -124,8 +144,9 @@ export function Picker({
|
|
|
124
144
|
// line so a multi-line description (e.g. ToolApproval) cannot push the title
|
|
125
145
|
// off the top. The slot is always reserved so panel chrome is a constant 6
|
|
126
146
|
// rows (title + blank + desc + blank + 2 border), matching PICKER_CHROME_ROWS.
|
|
147
|
+
// With no hint row under the URL, the description row carries the copy note.
|
|
127
148
|
const panelDescription = truncateText(
|
|
128
|
-
String(description || '')
|
|
149
|
+
String(!urlLayout.hint && detailText ? signInUrlNote(copyUrl.status) : description || '')
|
|
129
150
|
.replace(/\s+/g, ' ')
|
|
130
151
|
.trim(),
|
|
131
152
|
Math.max(0, columns - 4)
|
|
@@ -134,6 +155,7 @@ export function Picker({
|
|
|
134
155
|
if (items.length === 0) {
|
|
135
156
|
const loadingRows = loading
|
|
136
157
|
? Array.from({ length: visibleLimit }, (_, index) => (
|
|
158
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: identical blank placeholder rows have no identity besides position
|
|
137
159
|
<Text key={`loading-${index}`} color={theme.inactive}>
|
|
138
160
|
{' '}
|
|
139
161
|
</Text>
|
|
@@ -253,6 +275,7 @@ export function Picker({
|
|
|
253
275
|
const attachConfirm = hasConfirm && index === footerLines.length - 1;
|
|
254
276
|
return (
|
|
255
277
|
<Box
|
|
278
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: footer lines are positional and may repeat text
|
|
256
279
|
key={`footer-${index}`}
|
|
257
280
|
flexDirection="row"
|
|
258
281
|
width="100%"
|
|
@@ -277,6 +300,7 @@ export function Picker({
|
|
|
277
300
|
</>
|
|
278
301
|
) : null}
|
|
279
302
|
</Box>
|
|
303
|
+
<SignInUrl url={detailText} layout={urlLayout} status={copyUrl.status} />
|
|
280
304
|
</Box>
|
|
281
305
|
);
|
|
282
306
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* components/SignInUrl.jsx — a sign-in URL shown OUTSIDE any bordered panel.
|
|
3
|
+
*
|
|
4
|
+
* Inside a bordered panel a wrapped URL carries the `│` border and padding on
|
|
5
|
+
* every row, so a terminal selection copies junk. Here the rows are plain text
|
|
6
|
+
* with no border, the URL is an OSC 8 hyperlink whose target is the complete
|
|
7
|
+
* URL (click it, or "Copy link" in the terminal), and SIGN_IN_COPY_KEY copies the
|
|
8
|
+
* exact URL through the clipboard helper. `layout` (sign-in-url-layout.mjs) may shorten
|
|
9
|
+
* the visible label to fit the panel; the link target and the copy stay complete.
|
|
10
|
+
*/
|
|
11
|
+
import { useState } from 'react';
|
|
12
|
+
import { Box, Text } from 'ink';
|
|
13
|
+
import { theme } from '../theme.mjs';
|
|
14
|
+
import { copyToClipboard } from '../app/clipboard.mjs';
|
|
15
|
+
|
|
16
|
+
const SAFE_URL_RE = /^https?:\/\//i;
|
|
17
|
+
|
|
18
|
+
// OSC 8 needs no support probe: a terminal that does not know the hyperlink
|
|
19
|
+
// sequence ignores it and shows the label (its documented fallback). Only
|
|
20
|
+
// http(s) targets are linked.
|
|
21
|
+
const linkedUrl = (url, label) => (SAFE_URL_RE.test(url) ? `\x1b]8;;${url}\x07${label}\x1b]8;;\x07` : label);
|
|
22
|
+
|
|
23
|
+
const STATUS_TEXT = {
|
|
24
|
+
copied: 'URL copied',
|
|
25
|
+
sent: 'Sent to terminal clipboard (not confirmed)',
|
|
26
|
+
failed: 'Copy failed: no clipboard available, use the link above',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** The one key that copies a sign-in URL in every panel, with or without a focused text input. */
|
|
30
|
+
const SIGN_IN_COPY_KEY = 'Ctrl+Y';
|
|
31
|
+
export const isSignInCopyKey = (input, key) => Boolean(key?.ctrl) && String(input || '').toLowerCase() === 'y';
|
|
32
|
+
|
|
33
|
+
/** The copy-key note, or what the last copy did. */
|
|
34
|
+
export const signInUrlNote = (status) => STATUS_TEXT[status] || `${SIGN_IN_COPY_KEY} copies the full URL`;
|
|
35
|
+
|
|
36
|
+
/** { copy, status } — status is '', 'copied', 'sent' (OSC 52 only) or 'failed'. */
|
|
37
|
+
export function useCopyUrl(url, copyText = (text) => copyToClipboard(text, { confirm: true })) {
|
|
38
|
+
const [status, setStatus] = useState('');
|
|
39
|
+
const copy = () => {
|
|
40
|
+
if (!url) return;
|
|
41
|
+
Promise.resolve()
|
|
42
|
+
.then(() => copyText(url))
|
|
43
|
+
.then((how) => setStatus(how === 'terminal' ? 'sent' : 'copied'))
|
|
44
|
+
.catch(() => setStatus('failed'));
|
|
45
|
+
};
|
|
46
|
+
return { copy, status };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function SignInUrl({ url, layout, status = '' }) {
|
|
50
|
+
const text = String(url || '').trim();
|
|
51
|
+
if (!text || layout.rows === 0) return null;
|
|
52
|
+
return (
|
|
53
|
+
// flexShrink 0 everywhere: the layout reserved exactly these rows, and in a
|
|
54
|
+
// height-constrained panel (fillHeight) yoga would otherwise shrink the
|
|
55
|
+
// wrapped URL Text below its row count, so the hint row lands on the
|
|
56
|
+
// URL's last row. The panel's bordered box gives the rows up instead.
|
|
57
|
+
<Box flexDirection="column" flexShrink={0} width="100%">
|
|
58
|
+
<Text color={theme.text} wrap={layout.compact ? 'truncate-end' : 'wrap'} flexShrink={0}>
|
|
59
|
+
{linkedUrl(text, layout.label)}
|
|
60
|
+
</Text>
|
|
61
|
+
{layout.hint ? (
|
|
62
|
+
<Text color={theme.subtle} wrap="truncate-end" flexShrink={0}>
|
|
63
|
+
{signInUrlNote(status)}
|
|
64
|
+
</Text>
|
|
65
|
+
) : null}
|
|
66
|
+
</Box>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -9,6 +9,8 @@ import { clearSelection, lineEnd, lineStart, moveCursor, replaceSelection, selec
|
|
|
9
9
|
import { textEntryReservedRows } from '../app/text-layout.mjs';
|
|
10
10
|
import { createTextEntryCursorAnchor, singleLine } from './text-entry-layout.mjs';
|
|
11
11
|
import { renderTextEntryValue } from './text-entry-value.jsx';
|
|
12
|
+
import { SignInUrl, isSignInCopyKey, signInUrlNote, useCopyUrl } from './SignInUrl.jsx';
|
|
13
|
+
import { signInUrlLayout } from './sign-in-url-layout.mjs';
|
|
12
14
|
import { canSubmitTextEntry } from '../app/text-entry-policy.mjs';
|
|
13
15
|
import { truncatePanelText as truncateText } from './panel-cell-text.mjs';
|
|
14
16
|
import { applyEditChord, createVerticalMover } from './prompt-input/edit-chords.mjs';
|
|
@@ -23,10 +25,53 @@ import {
|
|
|
23
25
|
} from './prompt-input/edit-helpers.mjs';
|
|
24
26
|
import { isCsiPrivateReply } from './prompt-input/key-signals.mjs';
|
|
25
27
|
|
|
28
|
+
/** The bordered panel: title row, hint row and the editable value. `boxRef`
|
|
29
|
+
* is the value box the cursor anchor is installed on. */
|
|
30
|
+
function TextEntryFrame({ title, helpText, hintText, multiline, promptLabel, boxRef, contentHeight, renderedValue }) {
|
|
31
|
+
return (
|
|
32
|
+
<Box borderStyle="round" borderColor={theme.promptBorder} paddingX={1} width="100%" flexDirection="column">
|
|
33
|
+
<Box flexDirection="row" justifyContent="space-between">
|
|
34
|
+
<Text color={theme.panelTitle}>{title}</Text>
|
|
35
|
+
<Text color={theme.subtle}>{helpText}</Text>
|
|
36
|
+
</Box>
|
|
37
|
+
<Text> </Text>
|
|
38
|
+
<Text color={theme.subtle}>{hintText || ' '}</Text>
|
|
39
|
+
<Text> </Text>
|
|
40
|
+
{multiline ? (
|
|
41
|
+
<Box flexDirection="row" width="100%" alignItems="flex-start" backgroundColor={surfaceBackground()}>
|
|
42
|
+
<Text color={theme.inactive}>{promptLabel}</Text>
|
|
43
|
+
<Box
|
|
44
|
+
ref={boxRef}
|
|
45
|
+
flexGrow={1}
|
|
46
|
+
flexDirection="column"
|
|
47
|
+
height={contentHeight}
|
|
48
|
+
overflow="hidden"
|
|
49
|
+
backgroundColor={surfaceBackground()}
|
|
50
|
+
>
|
|
51
|
+
<Text color={theme.text} wrap="hard">
|
|
52
|
+
{renderedValue}
|
|
53
|
+
</Text>
|
|
54
|
+
</Box>
|
|
55
|
+
</Box>
|
|
56
|
+
) : (
|
|
57
|
+
<Box ref={boxRef} flexDirection="row" width="100%" backgroundColor={surfaceBackground()}>
|
|
58
|
+
<Text color={theme.inactive}>{promptLabel}</Text>
|
|
59
|
+
<Text color={theme.text} wrap="truncate">
|
|
60
|
+
{renderedValue}
|
|
61
|
+
</Text>
|
|
62
|
+
</Box>
|
|
63
|
+
)}
|
|
64
|
+
</Box>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
26
68
|
export function TextEntryPanel({
|
|
27
69
|
title,
|
|
28
70
|
hint = '',
|
|
29
71
|
detail = '',
|
|
72
|
+
copyText,
|
|
73
|
+
// Rows the panel can give the detail URL block (row-budgets.mjs); unbounded when unknown.
|
|
74
|
+
urlMaxRows = Number.POSITIVE_INFINITY,
|
|
30
75
|
initialValue = '',
|
|
31
76
|
// Blank submit = the documented reset/clear action for some prompt kinds
|
|
32
77
|
// (system shell → auto, provider auto-clear → built-in, profile title →
|
|
@@ -56,6 +101,11 @@ export function TextEntryPanel({
|
|
|
56
101
|
const preferredColumnRef = useRef(null);
|
|
57
102
|
const { isRawModeSupported } = useStdin();
|
|
58
103
|
draftRef.current = draft;
|
|
104
|
+
// Optional sign-in URL (e.g. the OAuth URL), drawn complete and borderless
|
|
105
|
+
// under the panel — in the live panel only, never pushed to the transcript, so
|
|
106
|
+
// it disappears when the panel closes instead of lingering in scrollback.
|
|
107
|
+
const detailText = String(detail || '').trim();
|
|
108
|
+
const copyUrl = useCopyUrl(detailText, copyText);
|
|
59
109
|
|
|
60
110
|
const flushImmediate = () => {
|
|
61
111
|
let node = boxRef.current;
|
|
@@ -81,6 +131,7 @@ export function TextEntryPanel({
|
|
|
81
131
|
|
|
82
132
|
const moveDraftVertically = createVerticalMover({ draftRef, contentWidthRef, preferredColumnRef, commitDraft });
|
|
83
133
|
|
|
134
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: reset only when the prompt (title/initialValue) changes; commitDraft is recreated every render and only touches refs/setState
|
|
84
135
|
useEffect(() => {
|
|
85
136
|
const value = String(initialValue || '');
|
|
86
137
|
commitDraft({ value, cursor: value.length, selectionAnchor: null });
|
|
@@ -133,6 +184,7 @@ export function TextEntryPanel({
|
|
|
133
184
|
(input, key) => {
|
|
134
185
|
const rawSource = String(input ?? '');
|
|
135
186
|
const rawInput = normalizePastedText(input);
|
|
187
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: matches the ESC byte of SGR mouse reports
|
|
136
188
|
if (/(?:\x1b)?\[<\d+;\d+;\d+[Mm]/.test(rawSource)) return;
|
|
137
189
|
// Safety net: drop CSI-private replies/fragments (\x1b[?<n>u / \x1b[?...c);
|
|
138
190
|
// a volunteered report must never type into the field.
|
|
@@ -230,6 +282,10 @@ export function TextEntryPanel({
|
|
|
230
282
|
updateDraft((d) => moveCursor(d, lineEnd(d.value, d.cursor), { extend: key.shift }));
|
|
231
283
|
return;
|
|
232
284
|
}
|
|
285
|
+
if (detailText && isSignInCopyKey(input, key)) {
|
|
286
|
+
copyUrl.copy();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
233
289
|
if (applyEditChord(key, inputKey, updateDraft)) return;
|
|
234
290
|
if (rawInput && !key.ctrl && !key.meta) {
|
|
235
291
|
updateDraft((d) => insertText(d, rawInput));
|
|
@@ -256,12 +312,14 @@ export function TextEntryPanel({
|
|
|
256
312
|
cursorEnabledRef.current = isRawModeSupported;
|
|
257
313
|
installCursorAnchor();
|
|
258
314
|
|
|
315
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: mount-only anchor install; the helpers are per-render closures over refs
|
|
259
316
|
useLayoutEffect(() => {
|
|
260
317
|
if (!installCursorAnchor()) return;
|
|
261
318
|
bumpCursorAnchorEpoch((epoch) => epoch + 1);
|
|
262
319
|
queueMicrotask(flushImmediate);
|
|
263
320
|
}, []);
|
|
264
321
|
|
|
322
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: title/multiline/draft are deliberate triggers to re-flush after content changes; flushImmediate is a per-render ref closure
|
|
265
323
|
useLayoutEffect(() => {
|
|
266
324
|
if (!isRawModeSupported) return;
|
|
267
325
|
queueMicrotask(flushImmediate);
|
|
@@ -279,55 +337,27 @@ export function TextEntryPanel({
|
|
|
279
337
|
// Standard panel rhythm: title row, blank, single-line hint, blank, content.
|
|
280
338
|
// The hint is collapsed to one line and width-truncated so a long manual
|
|
281
339
|
// OAuth URL can never wrap and push the bordered title off the top.
|
|
282
|
-
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
340
|
+
// The URL block gets the rows the panel really has (urlMaxRows) and degrades
|
|
341
|
+
// to fit; with no hint row under the URL, the hint row carries the copy note.
|
|
342
|
+
const urlLayout = signInUrlLayout(detailText, columns, urlMaxRows);
|
|
343
|
+
const hintText = truncateText(
|
|
344
|
+
!urlLayout.hint && detailText ? signInUrlNote(copyUrl.status) : singleLine(hint),
|
|
345
|
+
Math.max(0, columns - 4)
|
|
346
|
+
);
|
|
287
347
|
|
|
288
348
|
return (
|
|
289
349
|
<Box flexDirection="column" flexShrink={0} width="100%">
|
|
290
|
-
<
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
{
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
{detailText}
|
|
302
|
-
</Text>
|
|
303
|
-
</Box>
|
|
304
|
-
) : null}
|
|
305
|
-
<Text> </Text>
|
|
306
|
-
{multiline ? (
|
|
307
|
-
<Box flexDirection="row" width="100%" alignItems="flex-start" backgroundColor={surfaceBackground()}>
|
|
308
|
-
<Text color={theme.inactive}>{promptLabel}</Text>
|
|
309
|
-
<Box
|
|
310
|
-
ref={boxRef}
|
|
311
|
-
flexGrow={1}
|
|
312
|
-
flexDirection="column"
|
|
313
|
-
height={contentHeight}
|
|
314
|
-
overflow="hidden"
|
|
315
|
-
backgroundColor={surfaceBackground()}
|
|
316
|
-
>
|
|
317
|
-
<Text color={theme.text} wrap="hard">
|
|
318
|
-
{renderedValue}
|
|
319
|
-
</Text>
|
|
320
|
-
</Box>
|
|
321
|
-
</Box>
|
|
322
|
-
) : (
|
|
323
|
-
<Box ref={boxRef} flexDirection="row" width="100%" backgroundColor={surfaceBackground()}>
|
|
324
|
-
<Text color={theme.inactive}>{promptLabel}</Text>
|
|
325
|
-
<Text color={theme.text} wrap="truncate">
|
|
326
|
-
{renderedValue}
|
|
327
|
-
</Text>
|
|
328
|
-
</Box>
|
|
329
|
-
)}
|
|
330
|
-
</Box>
|
|
350
|
+
<TextEntryFrame
|
|
351
|
+
title={title}
|
|
352
|
+
helpText={helpText}
|
|
353
|
+
hintText={hintText}
|
|
354
|
+
multiline={multiline}
|
|
355
|
+
promptLabel={promptLabel}
|
|
356
|
+
boxRef={boxRef}
|
|
357
|
+
contentHeight={contentHeight}
|
|
358
|
+
renderedValue={renderedValue}
|
|
359
|
+
/>
|
|
360
|
+
<SignInUrl url={detailText} layout={urlLayout} status={copyUrl.status} />
|
|
331
361
|
</Box>
|
|
332
362
|
);
|
|
333
363
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// components/sign-in-url-layout.mjs — row budget for a borderless sign-in URL block.
|
|
2
|
+
//
|
|
3
|
+
// `maxRows` is the height the panel can actually give the block. The block
|
|
4
|
+
// degrades until it fits, and ONLY the visible text shrinks: the OSC 8 link
|
|
5
|
+
// target and the clipboard copy always use the full URL.
|
|
6
|
+
// 1. full URL wrapped over its rows + a copy-hint row
|
|
7
|
+
// 2. one row with the URL's start and `…` + the hint row
|
|
8
|
+
// 3. that one row alone (hint dropped)
|
|
9
|
+
// 4. nothing (0 rows); the panel's own description/hint row carries the copy note
|
|
10
|
+
import { wrappedDetailRows } from '../app/text-layout.mjs';
|
|
11
|
+
import { truncatePanelText } from './panel-cell-text.mjs';
|
|
12
|
+
|
|
13
|
+
export function signInUrlLayout(url, columns, maxRows) {
|
|
14
|
+
const text = String(url || '').trim();
|
|
15
|
+
if (!text) return { compact: false, rows: 0, hint: false, label: '' };
|
|
16
|
+
const width = Math.max(1, Math.floor(Number(columns) || 1));
|
|
17
|
+
const max = Math.max(0, Math.floor(Number(maxRows)));
|
|
18
|
+
const fullRows = wrappedDetailRows(text, width) + 1;
|
|
19
|
+
if (fullRows <= max) return { compact: false, rows: fullRows, hint: true, label: text };
|
|
20
|
+
const label = truncatePanelText(text, width);
|
|
21
|
+
if (max >= 2) return { compact: true, rows: 2, hint: true, label };
|
|
22
|
+
if (max >= 1) return { compact: true, rows: 1, hint: false, label };
|
|
23
|
+
return { compact: true, rows: 0, hint: false, label: '' };
|
|
24
|
+
}
|