remo-cli 2.2.0rc5__tar.gz → 2.2.0rc6__tar.gz
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.
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/PKG-INFO +1 -1
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docs/web-session-interface.md +6 -2
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/AppShell.css +7 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/AppShell.tsx +38 -5
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/ShortcutsModal.tsx +2 -1
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/TerminalCard.css +50 -7
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/TerminalCard.tsx +75 -26
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/WorkspacePane.tsx +33 -6
- remo_cli-2.2.0rc6/frontend/src/lib/fullscreen.ts +38 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/state/useConsoleKeyboard.ts +26 -2
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/state/workspace.ts +58 -9
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/pyproject.toml +1 -1
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/uv.lock +1 -1
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-analyze/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-checklist/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-clarify/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-constitution/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-converge/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-git-commit/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-git-feature/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-git-initialize/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-git-remote/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-git-validate/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-implement/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-plan/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-specify/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-tasks/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.claude/skills/speckit-taskstoissues/SKILL.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.devcontainer/devcontainer.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.dockerignore +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.github/dependabot.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.github/workflows/ci.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.github/workflows/provision.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.github/workflows/release.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.github/workflows/smoke-test-cleanup.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.github/workflows/smoke-test.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.github/workflows/teardown.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.gitignore +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.maverick/checkpoints/feature/implementation_complete.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.maverick/checkpoints/feature/phase_${{ index }}_complete.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.maverick/checkpoints/feature/validation_complete.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/.registry +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/README.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/commands/speckit.git.commit.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/commands/speckit.git.feature.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/commands/speckit.git.initialize.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/commands/speckit.git.remote.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/commands/speckit.git.validate.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/config-template.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/extension.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/git-config.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/scripts/bash/auto-commit.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/scripts/bash/create-new-feature.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/scripts/bash/git-common.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/scripts/bash/initialize-repo.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/scripts/powershell/git-common.ps1 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/extensions.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/feature.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/init-options.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/integration.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/integrations/claude.manifest.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/integrations/speckit.manifest.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/memory/constitution.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/scripts/bash/check-prerequisites.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/scripts/bash/common.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/scripts/bash/create-new-feature.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/scripts/bash/setup-plan.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/scripts/bash/setup-tasks.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/scripts/bash/update-agent-context.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/templates/agent-file-template.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/templates/checklist-template.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/templates/constitution-template.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/templates/plan-template.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/templates/spec-template.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/templates/tasks-template.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/workflows/speckit/workflow.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/.specify/workflows/workflow-registry.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/AGENTS.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/CLAUDE.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/CONTRIBUTING.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/Dockerfile +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/LICENSE +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/README.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/.ansible-lint +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/README.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/ansible.cfg +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/aws_configure.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/aws_resize.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/aws_site.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/aws_teardown.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/group_vars/all.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/group_vars/incus_containers.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/group_vars/proxmox_containers.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/hetzner_configure.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/hetzner_provision.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/hetzner_resize.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/hetzner_site.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/hetzner_teardown.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/incus_bootstrap.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/incus_configure.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/incus_provision.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/incus_resize.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/incus_site.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/incus_teardown.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/inventory/hosts.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/inventory/incus_containers.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/proxmox_bootstrap.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/proxmox_configure.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/proxmox_provision.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/proxmox_resize.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/proxmox_site.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/proxmox_teardown.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/requirements.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/aws_server/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/aws_server/tasks/ebs.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/aws_server/tasks/ec2.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/aws_server/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/aws_server/tasks/patch_manager.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/aws_server/tasks/preflight.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/aws_server/tasks/security_group.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/deacon/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/deacon/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/devcontainers/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/docker/handlers/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/docker/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/fzf/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/github_cli/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/hetzner_server/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/hetzner_server/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_bootstrap/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_bootstrap/handlers/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_bootstrap/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_container/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_container/handlers/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_container/meta/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_container/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_container/tasks/preflight.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_container_teardown/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_container_teardown/meta/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/incus_container_teardown/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/nodejs/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/nodejs/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_bootstrap/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_bootstrap/meta/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_bootstrap/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_container/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_container/handlers/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_container/meta/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_container/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_container/tasks/preflight.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_container_teardown/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_container_teardown/meta/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/proxmox_container_teardown/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/user_setup/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/user_setup/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/user_setup/templates/devshell.sh.j2 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/user_setup/templates/project-launch.sh.j2 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/user_setup/templates/project-menu.sh.j2 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/user_setup/templates/remo-host.sh.j2 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/zellij/defaults/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/zellij/tasks/main.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/zellij/templates/config.kdl.j2 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/roles/zellij/templates/layouts/minimal.kdl.j2 +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/ansible/tasks/configure_dev_tools.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docker/Dockerfile +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docker/compose.example.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docker/entrypoint.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docs/aws.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docs/hetzner.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docs/incus.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docs/install.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docs/proxmox.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docs/remo-web-speckit-input.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/docs/remo-web.html +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/index.html +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/package-lock.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/package.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/public/README.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/public/dist__ghostty-vt.wasm +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/public/ghostty-vt.wasm +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/scripts/copy-ghostty-wasm.mjs +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/api/client.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/AppRoot.tsx +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/AwaitingAdoption.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/AwaitingAdoption.tsx +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/OfflineOverlay.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/OfflineOverlay.tsx +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/PairToSync.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/PairToSync.tsx +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/SessionRail.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/SessionRail.tsx +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/SettingsPage.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/SettingsPage.tsx +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/ShortcutsModal.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/TopBar.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/TopBar.tsx +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/WorkspacePane.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/providerMeta.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/components/railModel.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/lib/clipboard.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/main.tsx +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/state/discovery.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/state/fonts.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/state/health.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/state/settings.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/terminal/.gitkeep +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/terminal/GhosttyRenderer.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/terminal/RendererAdapter.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/terminal/TerminalConnection.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/terminal/XtermRenderer.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/terminal/defaultRenderer.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/theme/fonts.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/src/theme/tokens.css +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/tsconfig.json +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/frontend/vite.config.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/install.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/maverick.yaml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/playwright.config.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/project-menu.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/remo +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/run.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/scripts/setup-aws-oidc.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/checklists/requirements.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/checklists/validation.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/contracts/playbook-interface.yaml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/contracts/role-interface.yaml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/data-model.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/plan.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/quickstart.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/research.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/spec.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/001-bootstrap-incus-host/tasks.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/checklists/requirements.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/contracts/playbook-interface.yaml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/contracts/role-interface.yaml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/data-model.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/plan.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/quickstart.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/research.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/spec.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/002-incus-container-support/tasks.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/003-python-cli-rewrite/checklists/requirements.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/003-python-cli-rewrite/data-model.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/003-python-cli-rewrite/plan.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/003-python-cli-rewrite/quickstart.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/003-python-cli-rewrite/research.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/003-python-cli-rewrite/spec.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/003-python-cli-rewrite/tasks.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/004-proxmox-container-support/spec.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/005-provider-snapshots/checklists/requirements.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/005-provider-snapshots/contracts/cli-surface.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/005-provider-snapshots/data-model.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/005-provider-snapshots/plan.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/005-provider-snapshots/quickstart.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/005-provider-snapshots/research.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/005-provider-snapshots/spec.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/005-provider-snapshots/tasks.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/checklists/requirements.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/contracts/remo-host-protocol.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/contracts/rest-api.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/contracts/terminal-websocket.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/data-model.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/plan.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/quickstart.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/research.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/spec.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/010-web-session-interface/tasks.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/checklists/requirements.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/contracts/cli-web-adopt.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/contracts/setup-api.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/data-model.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/plan.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/quickstart-results.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/quickstart.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/research.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/spec.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/011-web-adopt/tasks.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/contracts/cli-web-adopt.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/contracts/pairing-api.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/contracts/setup-api.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/data-model.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/plan.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/quickstart.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/research.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/spec.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/specs/012-web-adopt-pairing/tasks.md +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/__main__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/cp.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/main.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/providers/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/providers/aws.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/providers/hetzner.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/providers/incus.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/providers/proxmox.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/shell.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/cli/web.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/ansible_runner.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/completion.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/config.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/known_hosts.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/output.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/picker.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/remo_host_client.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/rsync.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/ssh.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/validation.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/version.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/core/web_adopt.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/models/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/models/capability.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/models/discovery.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/models/host.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/models/session_target.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/models/snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/providers/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/providers/aws.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/providers/hetzner.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/providers/incus.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/providers/proxmox.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/api/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/api/hosts.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/api/pairing.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/api/setup.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/api/terminals.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/app.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/check.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/config.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/discovery.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/health.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/logging_config.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/models.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/operator_auth.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/pairing.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/ssh_master.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/state.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/terminal.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/terminal_registry.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/src/remo_cli/web/tokens.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/ansible/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/ansible/test_remo_host_idempotency.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/conftest.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/e2e/fixtures.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/e2e/ghostty-shell-io.spec.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/e2e/ghostty-tui-compatibility.spec.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/e2e/keyboard-routing.spec.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/e2e/mobile.spec.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/e2e/reconnect.spec.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/e2e/workspace-layout.spec.ts +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/image/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/image/test_docker_image.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/_nine_terminal_fixture.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/orbstack-cloud-init.yml +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/orbstack.sh +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/test_nine_terminals.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/test_nine_terminals_soak.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/test_remo_host_e2e.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/test_security_rejections.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/test_terminal_attach.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/test_web_adopt_e2e.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/integration/test_web_cli_parity.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/perf/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/perf/test_latency.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/providers/test_aws_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/providers/test_hetzner_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/providers/test_incus_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/providers/test_proxmox_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/test_cp.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/test_main.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/test_shell.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/cli/test_web_adopt_cmd.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_ansible_runner.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_config.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_known_hosts.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_output.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_picker.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_remo_host_client.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_rsync.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_ssh.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_ssh_controlpath.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_ssh_identity_opts.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_validation.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_version.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_web_adopt_authorize.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_web_adopt_code.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_web_adopt_payload.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_web_adopt_trust.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/core/test_web_push.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/models/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/models/test_capability.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/models/test_discovery.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/models/test_session_target.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/providers/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/providers/test_aws_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/providers/test_hetzner_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/providers/test_incus_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/providers/test_proxmox_devcontainer_runtime.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/providers/test_proxmox_snapshot.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/test_ansible_templates.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/test_host_model.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/__init__.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/_pairing_support.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/conftest.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_backpressure.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_check.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_csp.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_discovery.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_health_states.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_hosts_mapping.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_lazy_import.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_log_redaction.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_mint_gating.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_operator_auth.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_pairing.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_pairing_origin.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_setup_api.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_setup_auth.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_setup_dormancy.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_state.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_terminal_resize.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_terminals_api.py +0 -0
- {remo_cli-2.2.0rc5 → remo_cli-2.2.0rc6}/tests/unit/web/test_tokens.py +0 -0
|
@@ -102,8 +102,12 @@ The SPA is a two-pane **web console**:
|
|
|
102
102
|
row (or its `+` button) **adds** it to a responsive grid (1/2/3 columns by count). Clicking a grid
|
|
103
103
|
tile solos it; **Esc** collapses the grid back to the focused terminal; number keys **1–9** jump to
|
|
104
104
|
the numbered sessions (⌘ 1–9 add to the grid). Hidden terminals stay connected and keep their
|
|
105
|
-
scrollback. Each terminal header shows `provider · instance · region`, connection state, and
|
|
106
|
-
|
|
105
|
+
scrollback. Each terminal header shows `provider · instance · region`, connection state, and a
|
|
106
|
+
window-control cluster of the display modes — **▭ Normal** (single view), **⊞ Grid** (the grid, when
|
|
107
|
+
one is available), **⤢ Fullscreen** (the terminal fills the whole window — shell chrome hidden, plus
|
|
108
|
+
best-effort browser fullscreen), and **✕ Close** — with the current mode shown active. Press **f** to
|
|
109
|
+
toggle fullscreen on the focused terminal; **Esc** exits it. Fullscreen is a presentation overlay: it
|
|
110
|
+
never disturbs the single/grid layout underneath, so exiting returns to exactly where you were.
|
|
107
111
|
|
|
108
112
|
**Session-row glyphs** (also shown in the rail legend):
|
|
109
113
|
|
|
@@ -32,6 +32,13 @@
|
|
|
32
32
|
display: flex;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/* Fullscreen terminal: the shell collapses to just the pane. The rail/divider/
|
|
36
|
+
* backbar are already withheld from render (see AppShell); here we drop the top
|
|
37
|
+
* bar so the maximized terminal fills the whole viewport. */
|
|
38
|
+
.app-shell--maximized .topbar {
|
|
39
|
+
display: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
.app-rail {
|
|
36
43
|
flex: none;
|
|
37
44
|
flex-direction: column;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { useCallback, useEffect, useMemo, useState, type PointerEvent } from "react";
|
|
7
7
|
import type { SessionTarget } from "../api/client";
|
|
8
|
+
import { exitBrowserFullscreen } from "../lib/fullscreen";
|
|
8
9
|
import { useDiscovery } from "../state/discovery";
|
|
9
10
|
import { useHealth } from "../state/health";
|
|
10
11
|
import { settingsActions, useSettings } from "../state/settings";
|
|
@@ -42,6 +43,36 @@ export function AppShell(): JSX.Element {
|
|
|
42
43
|
return () => window.removeEventListener("resize", onResize);
|
|
43
44
|
}, []);
|
|
44
45
|
|
|
46
|
+
// A terminal is fullscreen when the overlay id still resolves to an attached
|
|
47
|
+
// card; that both hides the shell chrome (class below) and drives browser
|
|
48
|
+
// fullscreen. Entering browser fullscreen is requested from the click/keypress
|
|
49
|
+
// gesture (WorkspacePane / keyboard); here we own the two exit paths:
|
|
50
|
+
// (a) whenever the overlay clears through ANY path, leave browser fullscreen;
|
|
51
|
+
// (b) when the user leaves browser fullscreen natively (Esc/F11), clear the
|
|
52
|
+
// overlay so the shell chrome comes back.
|
|
53
|
+
const maximized =
|
|
54
|
+
workspace.maximizedId !== null && workspace.attached.includes(workspace.maximizedId);
|
|
55
|
+
const restore = workspace.restore;
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!maximized) {
|
|
59
|
+
exitBrowserFullscreen();
|
|
60
|
+
}
|
|
61
|
+
}, [maximized]);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (typeof document === "undefined") {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const onChange = (): void => {
|
|
68
|
+
if (!document.fullscreenElement && maximized) {
|
|
69
|
+
restore();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
document.addEventListener("fullscreenchange", onChange);
|
|
73
|
+
return () => document.removeEventListener("fullscreenchange", onChange);
|
|
74
|
+
}, [maximized, restore]);
|
|
75
|
+
|
|
45
76
|
const filters: RailFilters = useMemo(
|
|
46
77
|
() => ({ search, providerFilter, sessionOnly }),
|
|
47
78
|
[search, providerFilter, sessionOnly],
|
|
@@ -136,9 +167,11 @@ export function AppShell(): JSX.Element {
|
|
|
136
167
|
);
|
|
137
168
|
|
|
138
169
|
const paneHasContent = workspace.attached.length > 0;
|
|
139
|
-
|
|
170
|
+
// Fullscreen collapses the shell to just the maximized terminal: rail and
|
|
171
|
+
// divider gone, top bar hidden by the .app-shell--maximized class.
|
|
172
|
+
const railHidden = maximized || (narrow ? paneHasContent : settings.railCollapsed);
|
|
140
173
|
const paneHidden = narrow && !paneHasContent;
|
|
141
|
-
const showDivider = !narrow && !settings.railCollapsed;
|
|
174
|
+
const showDivider = !narrow && !settings.railCollapsed && !maximized;
|
|
142
175
|
|
|
143
176
|
// "Loading" until the first discovery has produced instances — either before
|
|
144
177
|
// the first poll (lastRefreshedAt null) or while a refresh is still in flight
|
|
@@ -151,7 +184,7 @@ export function AppShell(): JSX.Element {
|
|
|
151
184
|
const noCredentials = health.checks.ssh_identity === "missing";
|
|
152
185
|
|
|
153
186
|
return (
|
|
154
|
-
<div className="app-shell">
|
|
187
|
+
<div className={`app-shell${maximized ? " app-shell--maximized" : ""}`}>
|
|
155
188
|
<TopBar
|
|
156
189
|
showRailToggle={!narrow}
|
|
157
190
|
railCollapsed={settings.railCollapsed}
|
|
@@ -165,7 +198,7 @@ export function AppShell(): JSX.Element {
|
|
|
165
198
|
onShortcuts={onToggleShortcuts}
|
|
166
199
|
/>
|
|
167
200
|
|
|
168
|
-
{discovery.isRefreshing && (
|
|
201
|
+
{discovery.isRefreshing && !maximized && (
|
|
169
202
|
<div className="app-refresh-bar">
|
|
170
203
|
<div className="app-refresh-bar-fill" />
|
|
171
204
|
</div>
|
|
@@ -203,7 +236,7 @@ export function AppShell(): JSX.Element {
|
|
|
203
236
|
)}
|
|
204
237
|
|
|
205
238
|
<div className="app-pane" style={{ display: paneHidden ? "none" : "flex" }}>
|
|
206
|
-
{narrow && paneHasContent && (
|
|
239
|
+
{narrow && paneHasContent && !maximized && (
|
|
207
240
|
<div className="app-backbar">
|
|
208
241
|
<button
|
|
209
242
|
type="button"
|
|
@@ -9,7 +9,8 @@ interface ShortcutsModalProps {
|
|
|
9
9
|
const SHORTCUTS: { desc: string; key: string }[] = [
|
|
10
10
|
{ desc: "Open session 1–9 (single)", key: "1 – 9" },
|
|
11
11
|
{ desc: "Add session to grid", key: "⌘ 1–9" },
|
|
12
|
-
{ desc: "
|
|
12
|
+
{ desc: "Fullscreen focused terminal", key: "f" },
|
|
13
|
+
{ desc: "Exit fullscreen / collapse grid", key: "esc" },
|
|
13
14
|
{ desc: "Show this panel", key: "?" },
|
|
14
15
|
];
|
|
15
16
|
|
|
@@ -141,11 +141,49 @@
|
|
|
141
141
|
background: var(--bg-btn-hover);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
/* Window-control cluster: a tight row of square, icon-only mode buttons. */
|
|
145
|
+
.tc-winctl {
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
gap: 5px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.tc-btn--icon {
|
|
152
|
+
width: 26px;
|
|
153
|
+
height: 26px;
|
|
154
|
+
padding: 0;
|
|
155
|
+
display: inline-flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
justify-content: center;
|
|
158
|
+
font-size: 13px;
|
|
159
|
+
line-height: 1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.terminal-card--grid .tc-btn--icon {
|
|
145
163
|
width: 22px;
|
|
146
164
|
height: 22px;
|
|
147
|
-
|
|
148
|
-
|
|
165
|
+
font-size: 11px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Current display mode: highlighted (and disabled — you can't re-enter it). */
|
|
169
|
+
.tc-btn--active {
|
|
170
|
+
border-color: color-mix(in oklch, var(--accent) 55%, var(--border-strong));
|
|
171
|
+
background: color-mix(in oklch, var(--accent) 18%, transparent);
|
|
172
|
+
color: color-mix(in oklch, var(--accent) 82%, white);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Unavailable modes (e.g. Grid with no grid to show) read as inert. */
|
|
176
|
+
.tc-btn--icon:disabled {
|
|
177
|
+
cursor: default;
|
|
178
|
+
}
|
|
179
|
+
.tc-btn--icon:disabled:not(.tc-btn--active) {
|
|
180
|
+
opacity: 0.4;
|
|
181
|
+
}
|
|
182
|
+
.tc-btn--icon:disabled:hover {
|
|
183
|
+
background: var(--bg-btn);
|
|
184
|
+
}
|
|
185
|
+
.tc-btn--active:disabled:hover {
|
|
186
|
+
background: color-mix(in oklch, var(--accent) 18%, transparent);
|
|
149
187
|
}
|
|
150
188
|
|
|
151
189
|
.tc-btn--accent {
|
|
@@ -193,9 +231,14 @@
|
|
|
193
231
|
padding: 8px 13px;
|
|
194
232
|
font-size: 13px;
|
|
195
233
|
}
|
|
196
|
-
.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
234
|
+
.tc-btn--icon,
|
|
235
|
+
.terminal-card--grid .tc-btn--icon {
|
|
236
|
+
width: 34px;
|
|
237
|
+
height: 34px;
|
|
238
|
+
padding: 0;
|
|
239
|
+
font-size: 15px;
|
|
240
|
+
}
|
|
241
|
+
.tc-winctl {
|
|
242
|
+
gap: 7px;
|
|
200
243
|
}
|
|
201
244
|
}
|
|
@@ -38,6 +38,9 @@ const STATE_LABELS: Record<TerminalConnectionState, string> = {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export type TerminalCardMode = "single" | "grid";
|
|
41
|
+
/** Which of the mutually-exclusive display modes this card is currently in.
|
|
42
|
+
* Drives the window-control cluster's active/disabled state. */
|
|
43
|
+
export type TerminalViewState = "normal" | "grid" | "fullscreen";
|
|
41
44
|
|
|
42
45
|
interface TerminalCardProps {
|
|
43
46
|
target: SessionTarget;
|
|
@@ -50,11 +53,17 @@ interface TerminalCardProps {
|
|
|
50
53
|
isFocused: boolean;
|
|
51
54
|
/** 1-based position label shown on a grid tile. */
|
|
52
55
|
num?: number;
|
|
56
|
+
/** The display mode this card is currently in (window-control cluster state). */
|
|
57
|
+
viewState: TerminalViewState;
|
|
53
58
|
onClose: () => void;
|
|
54
59
|
/** Grid tile clicked → solo it (single view). */
|
|
55
60
|
onSolo?: () => void;
|
|
56
|
-
/**
|
|
57
|
-
|
|
61
|
+
/** Window-control "Normal": solo this terminal into the single view. */
|
|
62
|
+
onNormal: () => void;
|
|
63
|
+
/** Window-control "Grid": show the grid — omitted when none is available. */
|
|
64
|
+
onGrid?: () => void;
|
|
65
|
+
/** Window-control "Fullscreen": enter fullscreen, or exit if already in it. */
|
|
66
|
+
onToggleFullscreen: () => void;
|
|
58
67
|
/** Called when the user clicks into the surface (focus this terminal). */
|
|
59
68
|
onFocusRequest?: () => void;
|
|
60
69
|
/** Called when output arrives while this card is hidden (rail activity dot). */
|
|
@@ -80,9 +89,12 @@ export function TerminalCard({
|
|
|
80
89
|
isVisible,
|
|
81
90
|
isFocused,
|
|
82
91
|
num,
|
|
92
|
+
viewState,
|
|
83
93
|
onClose,
|
|
84
94
|
onSolo,
|
|
85
|
-
|
|
95
|
+
onNormal,
|
|
96
|
+
onGrid,
|
|
97
|
+
onToggleFullscreen,
|
|
86
98
|
onFocusRequest,
|
|
87
99
|
onActivity,
|
|
88
100
|
onEnded,
|
|
@@ -298,44 +310,81 @@ export function TerminalCard({
|
|
|
298
310
|
{STATE_LABELS[connectionState]}
|
|
299
311
|
</span>
|
|
300
312
|
<div className="terminal-card-controls">
|
|
301
|
-
{
|
|
313
|
+
{needsManualReconnect && (
|
|
302
314
|
<button
|
|
303
315
|
type="button"
|
|
304
|
-
className="tc-btn"
|
|
305
|
-
|
|
316
|
+
className="tc-btn tc-btn--accent"
|
|
317
|
+
data-testid={`terminal-reconnect-${target.id}`}
|
|
306
318
|
onClick={(e) => {
|
|
307
319
|
e.stopPropagation();
|
|
308
|
-
|
|
320
|
+
handleReconnect();
|
|
309
321
|
}}
|
|
310
322
|
>
|
|
311
|
-
|
|
323
|
+
↻ Reconnect
|
|
312
324
|
</button>
|
|
313
325
|
)}
|
|
314
|
-
{
|
|
326
|
+
{/* Window-control cluster: mutually-exclusive display modes (the
|
|
327
|
+
* current one shown active + disabled) plus close. Fullscreen toggles.
|
|
328
|
+
* Icons only; the label is the tooltip. */}
|
|
329
|
+
<div className="tc-winctl" role="group" aria-label="Terminal display mode">
|
|
315
330
|
<button
|
|
316
331
|
type="button"
|
|
317
|
-
className=
|
|
318
|
-
data-testid={`terminal-
|
|
332
|
+
className={`tc-btn tc-btn--icon${viewState === "normal" ? " tc-btn--active" : ""}`}
|
|
333
|
+
data-testid={`terminal-normal-${target.id}`}
|
|
334
|
+
title="Normal (single view)"
|
|
335
|
+
aria-label="Normal view"
|
|
336
|
+
aria-pressed={viewState === "normal"}
|
|
337
|
+
disabled={viewState === "normal"}
|
|
319
338
|
onClick={(e) => {
|
|
320
339
|
e.stopPropagation();
|
|
321
|
-
|
|
340
|
+
onNormal();
|
|
322
341
|
}}
|
|
323
342
|
>
|
|
324
|
-
|
|
343
|
+
▭
|
|
325
344
|
</button>
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
345
|
+
<button
|
|
346
|
+
type="button"
|
|
347
|
+
className={`tc-btn tc-btn--icon${viewState === "grid" ? " tc-btn--active" : ""}`}
|
|
348
|
+
data-testid={`terminal-grid-${target.id}`}
|
|
349
|
+
title="Grid view"
|
|
350
|
+
aria-label="Grid view"
|
|
351
|
+
aria-pressed={viewState === "grid"}
|
|
352
|
+
disabled={viewState === "grid" || !onGrid}
|
|
353
|
+
onClick={(e) => {
|
|
354
|
+
e.stopPropagation();
|
|
355
|
+
onGrid?.();
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
⊞
|
|
359
|
+
</button>
|
|
360
|
+
<button
|
|
361
|
+
type="button"
|
|
362
|
+
className={`tc-btn tc-btn--icon${viewState === "fullscreen" ? " tc-btn--active" : ""}`}
|
|
363
|
+
data-testid={`terminal-fullscreen-${target.id}`}
|
|
364
|
+
title={viewState === "fullscreen" ? "Exit fullscreen" : "Fullscreen"}
|
|
365
|
+
aria-label={viewState === "fullscreen" ? "Exit fullscreen" : "Fullscreen"}
|
|
366
|
+
aria-pressed={viewState === "fullscreen"}
|
|
367
|
+
onClick={(e) => {
|
|
368
|
+
e.stopPropagation();
|
|
369
|
+
onToggleFullscreen();
|
|
370
|
+
}}
|
|
371
|
+
>
|
|
372
|
+
{viewState === "fullscreen" ? "⤡" : "⤢"}
|
|
373
|
+
</button>
|
|
374
|
+
<button
|
|
375
|
+
type="button"
|
|
376
|
+
className="tc-btn tc-btn--icon tc-btn--close"
|
|
377
|
+
data-testid={`terminal-close-${target.id}`}
|
|
378
|
+
title="Close terminal — remote Zellij session stays alive"
|
|
379
|
+
aria-label="Close terminal"
|
|
380
|
+
onClick={(e) => {
|
|
381
|
+
e.stopPropagation();
|
|
382
|
+
handleClose();
|
|
383
|
+
}}
|
|
384
|
+
>
|
|
385
|
+
✕
|
|
386
|
+
</button>
|
|
387
|
+
</div>
|
|
339
388
|
</div>
|
|
340
389
|
</header>
|
|
341
390
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { useMemo } from "react";
|
|
6
6
|
import type { SessionTarget } from "../api/client";
|
|
7
|
+
import { requestBrowserFullscreen } from "../lib/fullscreen";
|
|
7
8
|
import { useWorkspace } from "../state/workspace";
|
|
8
9
|
import { TerminalCard } from "./TerminalCard";
|
|
9
10
|
import "./WorkspacePane.css";
|
|
@@ -43,7 +44,7 @@ export function WorkspacePane({
|
|
|
43
44
|
narrow,
|
|
44
45
|
}: WorkspacePaneProps): JSX.Element {
|
|
45
46
|
const workspace = useWorkspace();
|
|
46
|
-
const { attached, visible, focusedId, prevGrid } = workspace;
|
|
47
|
+
const { attached, visible, focusedId, prevGrid, maximizedId } = workspace;
|
|
47
48
|
|
|
48
49
|
// Only attached ids that still resolve to a live target get a card.
|
|
49
50
|
const attachedTargets = useMemo(
|
|
@@ -70,18 +71,40 @@ export function WorkspacePane({
|
|
|
70
71
|
);
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
// Fullscreen is an orthogonal overlay: it only takes effect when it still
|
|
75
|
+
// resolves to an attached card. When active, that one card fills the pane and
|
|
76
|
+
// the single↔grid layout underneath is left untouched (so exiting restores it).
|
|
77
|
+
const maximized =
|
|
78
|
+
maximizedId !== null && attached.includes(maximizedId) ? maximizedId : null;
|
|
79
|
+
|
|
80
|
+
// Cards render as single (full-bleed) while a card is maximized; otherwise the
|
|
81
|
+
// usual single↔grid split by how many are visible.
|
|
82
|
+
const mode = maximized || visible.length <= 1 ? "single" : "grid";
|
|
74
83
|
const cols = gridColumns(visible.length, narrow);
|
|
75
84
|
const rows = Math.max(1, Math.ceil(visible.length / cols));
|
|
76
|
-
|
|
85
|
+
// The Grid control is available when a grid can be shown — either the visible
|
|
86
|
+
// set is already a grid (fullscreen opened over one) or a grid was remembered.
|
|
87
|
+
const canGrid =
|
|
88
|
+
visible.filter((id) => attached.includes(id)).length > 1 ||
|
|
89
|
+
(prevGrid ?? []).filter((id) => attached.includes(id)).length > 1;
|
|
77
90
|
|
|
78
91
|
// 1-based position for each visible id (grid tile number badge).
|
|
79
92
|
const visibleIndex = new Map(visible.map((id, i) => [id, i + 1]));
|
|
80
93
|
|
|
94
|
+
const toggleFullscreen = (id: string): void => {
|
|
95
|
+
if (maximized === id) {
|
|
96
|
+
workspace.restore();
|
|
97
|
+
} else {
|
|
98
|
+
workspace.maximize(id);
|
|
99
|
+
// Request from within this click gesture so the browser allows it.
|
|
100
|
+
requestBrowserFullscreen();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
81
104
|
return (
|
|
82
105
|
<main className="workspace" data-testid="workspace">
|
|
83
106
|
<div
|
|
84
|
-
className={`workspace-body workspace-body--${mode}`}
|
|
107
|
+
className={`workspace-body workspace-body--${mode}${maximized ? " workspace-body--maximized" : ""}`}
|
|
85
108
|
style={
|
|
86
109
|
mode === "grid"
|
|
87
110
|
? {
|
|
@@ -92,7 +115,8 @@ export function WorkspacePane({
|
|
|
92
115
|
}
|
|
93
116
|
>
|
|
94
117
|
{attachedTargets.map((target) => {
|
|
95
|
-
const isVisible = visible.includes(target.id);
|
|
118
|
+
const isVisible = maximized ? target.id === maximized : visible.includes(target.id);
|
|
119
|
+
const viewState = maximized === target.id ? "fullscreen" : mode === "grid" ? "grid" : "normal";
|
|
96
120
|
return (
|
|
97
121
|
<TerminalCard
|
|
98
122
|
key={target.id}
|
|
@@ -102,9 +126,12 @@ export function WorkspacePane({
|
|
|
102
126
|
isVisible={isVisible}
|
|
103
127
|
isFocused={focusedId === target.id}
|
|
104
128
|
num={visibleIndex.get(target.id)}
|
|
129
|
+
viewState={viewState}
|
|
105
130
|
onClose={() => workspace.closeTerm(target.id)}
|
|
106
131
|
onSolo={mode === "grid" ? () => workspace.soloTile(target.id) : undefined}
|
|
107
|
-
|
|
132
|
+
onNormal={() => workspace.soloTile(target.id)}
|
|
133
|
+
onGrid={canGrid ? workspace.backToGrid : undefined}
|
|
134
|
+
onToggleFullscreen={() => toggleFullscreen(target.id)}
|
|
108
135
|
onFocusRequest={() => workspace.setFocused(target.id)}
|
|
109
136
|
onActivity={() => workspace.markUnread(target.id)}
|
|
110
137
|
onEnded={() => onTerminalEnded(target)}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Best-effort browser (F11-style) fullscreen helpers.
|
|
2
|
+
//
|
|
3
|
+
// These are a thin, defensive wrapper over the Fullscreen API. The web-app's
|
|
4
|
+
// "fullscreen" terminal mode is primarily an *app-viewport* takeover (the shell
|
|
5
|
+
// hides its chrome via a CSS class keyed on `workspace.maximizedId`); requesting
|
|
6
|
+
// true browser fullscreen on top of that is a best-effort enhancement:
|
|
7
|
+
//
|
|
8
|
+
// - `requestBrowserFullscreen()` MUST be called from within a user-gesture
|
|
9
|
+
// handler (a click or keydown) — browsers reject it otherwise. On rejection
|
|
10
|
+
// the app-viewport takeover still stands, so the caller needs no fallback.
|
|
11
|
+
// - Exiting is centralized in AppShell (an effect keyed on `maximizedId`), so
|
|
12
|
+
// every exit path — the restore button, `f`, Esc, closing the card, or
|
|
13
|
+
// opening another session — funnels through `exitBrowserFullscreen()`.
|
|
14
|
+
//
|
|
15
|
+
// Both calls are idempotent: they guard on `document.fullscreenElement`, so
|
|
16
|
+
// double-invoking (e.g. our exit effect firing after a native Esc already left
|
|
17
|
+
// fullscreen) is a no-op and never loops.
|
|
18
|
+
|
|
19
|
+
export function requestBrowserFullscreen(): void {
|
|
20
|
+
if (typeof document === "undefined") {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const el = document.documentElement;
|
|
24
|
+
if (!document.fullscreenElement && el.requestFullscreen) {
|
|
25
|
+
// Swallow rejections: no gesture / disallowed by the browser just means we
|
|
26
|
+
// stay in the app-viewport takeover.
|
|
27
|
+
void el.requestFullscreen().catch(() => {});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function exitBrowserFullscreen(): void {
|
|
32
|
+
if (typeof document === "undefined") {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (document.fullscreenElement && document.exitFullscreen) {
|
|
36
|
+
void document.exitFullscreen().catch(() => {});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 1–9 open that numbered session solo (single view)
|
|
4
4
|
// ⌘/Ctrl/Shift 1–9 add/toggle that session in the grid
|
|
5
|
-
//
|
|
6
|
-
//
|
|
5
|
+
// f toggle fullscreen on the focused terminal
|
|
6
|
+
// Esc close an open overlay, else exit fullscreen, else collapse
|
|
7
|
+
// the grid to the focused terminal
|
|
7
8
|
// ? toggle the shortcuts panel
|
|
8
9
|
//
|
|
9
10
|
// Ignored while typing in an <input>/<textarea> (e.g. the rail search).
|
|
10
11
|
|
|
11
12
|
import { useEffect } from "react";
|
|
12
13
|
import type { SessionTarget } from "../api/client";
|
|
14
|
+
import { requestBrowserFullscreen } from "../lib/fullscreen";
|
|
13
15
|
import type { UseWorkspaceResult } from "./workspace";
|
|
14
16
|
|
|
15
17
|
export interface ConsoleKeyboardConfig {
|
|
@@ -45,6 +47,11 @@ export function useConsoleKeyboard(config: ConsoleKeyboardConfig): void {
|
|
|
45
47
|
if (onEscapeOverlay()) {
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
50
|
+
// Exit fullscreen before touching the single/grid layout.
|
|
51
|
+
if (workspace.maximizedId) {
|
|
52
|
+
workspace.restore();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
48
55
|
// Collapse a grid to just the focused terminal.
|
|
49
56
|
if (workspace.visible.length > 1 && workspace.focusedId) {
|
|
50
57
|
workspace.soloTile(workspace.focusedId);
|
|
@@ -52,6 +59,23 @@ export function useConsoleKeyboard(config: ConsoleKeyboardConfig): void {
|
|
|
52
59
|
return;
|
|
53
60
|
}
|
|
54
61
|
|
|
62
|
+
// Fullscreen the focused terminal (toggle). Requesting browser fullscreen
|
|
63
|
+
// here works because a keydown is a user gesture.
|
|
64
|
+
if (e.key === "f" || e.key === "F") {
|
|
65
|
+
const id = workspace.focusedId;
|
|
66
|
+
if (!id) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
if (workspace.maximizedId === id) {
|
|
71
|
+
workspace.restore();
|
|
72
|
+
} else {
|
|
73
|
+
workspace.maximize(id);
|
|
74
|
+
requestBrowserFullscreen();
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
if (/^[1-9]$/.test(e.key)) {
|
|
56
80
|
const target = flatOpenable[Number(e.key) - 1];
|
|
57
81
|
if (!target) {
|