remo-cli 4.0.0__tar.gz → 4.1.0__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.
Files changed (641) hide show
  1. remo_cli-4.1.0/.release-please-manifest.json +3 -0
  2. {remo_cli-4.0.0 → remo_cli-4.1.0}/AGENTS.md +3 -1
  3. {remo_cli-4.0.0 → remo_cli-4.1.0}/CHANGELOG.md +15 -0
  4. {remo_cli-4.0.0 → remo_cli-4.1.0}/CLAUDE.md +3 -1
  5. {remo_cli-4.0.0 → remo_cli-4.1.0}/PKG-INFO +1 -1
  6. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/web-session-interface.md +10 -1
  7. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/TerminalCard.css +11 -4
  8. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/TerminalCard.test.tsx +57 -0
  9. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/TerminalCard.tsx +157 -3
  10. remo_cli-4.1.0/frontend/src/components/WorkspacePane.css +139 -0
  11. remo_cli-4.1.0/frontend/src/components/WorkspacePane.test.tsx +175 -0
  12. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/WorkspacePane.tsx +120 -35
  13. remo_cli-4.1.0/frontend/src/components/masterLayout.test.ts +201 -0
  14. remo_cli-4.1.0/frontend/src/components/masterLayout.ts +190 -0
  15. remo_cli-4.1.0/frontend/src/state/workspace.test.ts +296 -0
  16. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/workspace.ts +156 -8
  17. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/theme/terminalThemes.test.ts +51 -0
  18. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/theme/terminalThemes.ts +11 -2
  19. {remo_cli-4.0.0 → remo_cli-4.1.0}/pyproject.toml +1 -1
  20. remo_cli-4.1.0/scripts/palette-check.sh +67 -0
  21. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_discovery.py +57 -18
  22. {remo_cli-4.0.0 → remo_cli-4.1.0}/uv.lock +1 -1
  23. remo_cli-4.0.0/.release-please-manifest.json +0 -3
  24. remo_cli-4.0.0/frontend/src/components/WorkspacePane.css +0 -70
  25. remo_cli-4.0.0/frontend/src/state/workspace.test.ts +0 -98
  26. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/release/SKILL.md +0 -0
  27. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-analyze/SKILL.md +0 -0
  28. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-checklist/SKILL.md +0 -0
  29. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-clarify/SKILL.md +0 -0
  30. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-constitution/SKILL.md +0 -0
  31. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-converge/SKILL.md +0 -0
  32. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-git-commit/SKILL.md +0 -0
  33. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-git-feature/SKILL.md +0 -0
  34. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-git-initialize/SKILL.md +0 -0
  35. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-git-remote/SKILL.md +0 -0
  36. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-git-validate/SKILL.md +0 -0
  37. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-implement/SKILL.md +0 -0
  38. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-plan/SKILL.md +0 -0
  39. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-specify/SKILL.md +0 -0
  40. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-tasks/SKILL.md +0 -0
  41. {remo_cli-4.0.0 → remo_cli-4.1.0}/.claude/skills/speckit-taskstoissues/SKILL.md +0 -0
  42. {remo_cli-4.0.0 → remo_cli-4.1.0}/.devcontainer/devcontainer.json +0 -0
  43. {remo_cli-4.0.0 → remo_cli-4.1.0}/.dockerignore +0 -0
  44. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/CODEOWNERS +0 -0
  45. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/dependabot.yml +0 -0
  46. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/ci.yml +0 -0
  47. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/codeql.yml +0 -0
  48. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/dependency-review.yml +0 -0
  49. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/dev-build.yml +0 -0
  50. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/provision.yml +0 -0
  51. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/release-please.yml +0 -0
  52. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/release.yml +0 -0
  53. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/smoke-test-cleanup.yml +0 -0
  54. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/smoke-test.yml +0 -0
  55. {remo_cli-4.0.0 → remo_cli-4.1.0}/.github/workflows/teardown.yml +0 -0
  56. {remo_cli-4.0.0 → remo_cli-4.1.0}/.gitignore +0 -0
  57. {remo_cli-4.0.0 → remo_cli-4.1.0}/.maverick/checkpoints/feature/implementation_complete.json +0 -0
  58. {remo_cli-4.0.0 → remo_cli-4.1.0}/.maverick/checkpoints/feature/phase_${{ index }}_complete.json +0 -0
  59. {remo_cli-4.0.0 → remo_cli-4.1.0}/.maverick/checkpoints/feature/validation_complete.json +0 -0
  60. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/.registry +0 -0
  61. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/README.md +0 -0
  62. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.commit.md +0 -0
  63. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.feature.md +0 -0
  64. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.initialize.md +0 -0
  65. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.remote.md +0 -0
  66. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/commands/speckit.git.validate.md +0 -0
  67. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/config-template.yml +0 -0
  68. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/extension.yml +0 -0
  69. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/git-config.yml +0 -0
  70. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/scripts/bash/auto-commit.sh +0 -0
  71. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/scripts/bash/create-new-feature.sh +0 -0
  72. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/scripts/bash/git-common.sh +0 -0
  73. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/scripts/bash/initialize-repo.sh +0 -0
  74. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/scripts/powershell/auto-commit.ps1 +0 -0
  75. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/scripts/powershell/create-new-feature.ps1 +0 -0
  76. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/scripts/powershell/git-common.ps1 +0 -0
  77. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions/git/scripts/powershell/initialize-repo.ps1 +0 -0
  78. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/extensions.yml +0 -0
  79. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/feature.json +0 -0
  80. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/init-options.json +0 -0
  81. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/integration.json +0 -0
  82. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/integrations/claude.manifest.json +0 -0
  83. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/integrations/speckit.manifest.json +0 -0
  84. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/memory/constitution.md +0 -0
  85. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  86. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/scripts/bash/common.sh +0 -0
  87. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/scripts/bash/create-new-feature.sh +0 -0
  88. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/scripts/bash/setup-plan.sh +0 -0
  89. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/scripts/bash/setup-tasks.sh +0 -0
  90. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/scripts/bash/update-agent-context.sh +0 -0
  91. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/templates/agent-file-template.md +0 -0
  92. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/templates/checklist-template.md +0 -0
  93. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/templates/constitution-template.md +0 -0
  94. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/templates/plan-template.md +0 -0
  95. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/templates/spec-template.md +0 -0
  96. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/templates/tasks-template.md +0 -0
  97. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/workflows/speckit/workflow.yml +0 -0
  98. {remo_cli-4.0.0 → remo_cli-4.1.0}/.specify/workflows/workflow-registry.json +0 -0
  99. {remo_cli-4.0.0 → remo_cli-4.1.0}/CONTRIBUTING.md +0 -0
  100. {remo_cli-4.0.0 → remo_cli-4.1.0}/Dockerfile +0 -0
  101. {remo_cli-4.0.0 → remo_cli-4.1.0}/LICENSE +0 -0
  102. {remo_cli-4.0.0 → remo_cli-4.1.0}/README.md +0 -0
  103. {remo_cli-4.0.0 → remo_cli-4.1.0}/SECURITY.md +0 -0
  104. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/.ansible-lint +0 -0
  105. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/README.md +0 -0
  106. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/ansible.cfg +0 -0
  107. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/aws_configure.yml +0 -0
  108. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/aws_resize.yml +0 -0
  109. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/aws_site.yml +0 -0
  110. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/aws_teardown.yml +0 -0
  111. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/group_vars/all.yml +0 -0
  112. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/group_vars/incus_containers.yml +0 -0
  113. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/group_vars/proxmox_containers.yml +0 -0
  114. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/hetzner_configure.yml +0 -0
  115. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/hetzner_provision.yml +0 -0
  116. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/hetzner_resize.yml +0 -0
  117. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/hetzner_site.yml +0 -0
  118. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/hetzner_teardown.yml +0 -0
  119. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/incus_bootstrap.yml +0 -0
  120. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/incus_configure.yml +0 -0
  121. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/incus_provision.yml +0 -0
  122. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/incus_resize.yml +0 -0
  123. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/incus_site.yml +0 -0
  124. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/incus_teardown.yml +0 -0
  125. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/inventory/hosts.yml +0 -0
  126. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/inventory/incus_containers.yml +0 -0
  127. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/proxmox_bootstrap.yml +0 -0
  128. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/proxmox_configure.yml +0 -0
  129. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/proxmox_provision.yml +0 -0
  130. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/proxmox_resize.yml +0 -0
  131. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/proxmox_site.yml +0 -0
  132. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/proxmox_teardown.yml +0 -0
  133. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/requirements.yml +0 -0
  134. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/aws_server/defaults/main.yml +0 -0
  135. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/ebs.yml +0 -0
  136. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/ec2.yml +0 -0
  137. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/main.yml +0 -0
  138. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/patch_manager.yml +0 -0
  139. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/preflight.yml +0 -0
  140. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/aws_server/tasks/security_group.yml +0 -0
  141. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/deacon/defaults/main.yml +0 -0
  142. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/deacon/tasks/main.yml +0 -0
  143. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/devcontainers/tasks/main.yml +0 -0
  144. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/docker/handlers/main.yml +0 -0
  145. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/docker/tasks/main.yml +0 -0
  146. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/fzf/tasks/main.yml +0 -0
  147. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/github_cli/tasks/main.yml +0 -0
  148. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/hetzner_server/defaults/main.yml +0 -0
  149. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/hetzner_server/tasks/main.yml +0 -0
  150. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_bootstrap/defaults/main.yml +0 -0
  151. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_bootstrap/handlers/main.yml +0 -0
  152. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_bootstrap/tasks/main.yml +0 -0
  153. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_container/defaults/main.yml +0 -0
  154. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_container/handlers/main.yml +0 -0
  155. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_container/meta/main.yml +0 -0
  156. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_container/tasks/main.yml +0 -0
  157. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_container/tasks/preflight.yml +0 -0
  158. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_container_teardown/defaults/main.yml +0 -0
  159. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_container_teardown/meta/main.yml +0 -0
  160. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/incus_container_teardown/tasks/main.yml +0 -0
  161. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/nodejs/defaults/main.yml +0 -0
  162. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/nodejs/files/nodesource-repo.gpg.key +0 -0
  163. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/nodejs/tasks/main.yml +0 -0
  164. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_bootstrap/defaults/main.yml +0 -0
  165. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_bootstrap/meta/main.yml +0 -0
  166. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_bootstrap/tasks/main.yml +0 -0
  167. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_container/defaults/main.yml +0 -0
  168. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_container/handlers/main.yml +0 -0
  169. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_container/meta/main.yml +0 -0
  170. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_container/tasks/main.yml +0 -0
  171. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_container/tasks/preflight.yml +0 -0
  172. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_container_teardown/defaults/main.yml +0 -0
  173. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_container_teardown/meta/main.yml +0 -0
  174. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/proxmox_container_teardown/tasks/main.yml +0 -0
  175. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/user_setup/defaults/main.yml +0 -0
  176. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/user_setup/tasks/main.yml +0 -0
  177. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/user_setup/templates/devshell.sh.j2 +0 -0
  178. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/user_setup/templates/project-launch.sh.j2 +0 -0
  179. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/user_setup/templates/project-menu.sh.j2 +0 -0
  180. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/user_setup/templates/remo-host.sh.j2 +0 -0
  181. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/zellij/defaults/main.yml +0 -0
  182. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/zellij/tasks/main.yml +0 -0
  183. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/zellij/templates/config.kdl.j2 +0 -0
  184. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/roles/zellij/templates/layouts/minimal.kdl.j2 +0 -0
  185. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/tasks/configure_dev_tools.yml +0 -0
  186. {remo_cli-4.0.0 → remo_cli-4.1.0}/ansible/tasks/repair_apt_sources.yml +0 -0
  187. {remo_cli-4.0.0 → remo_cli-4.1.0}/docker/Dockerfile +0 -0
  188. {remo_cli-4.0.0 → remo_cli-4.1.0}/docker/compose.example.yml +0 -0
  189. {remo_cli-4.0.0 → remo_cli-4.1.0}/docker/entrypoint.sh +0 -0
  190. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/aws.md +0 -0
  191. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/feature-history.md +0 -0
  192. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/hetzner.md +0 -0
  193. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/incus.md +0 -0
  194. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/install.sh +0 -0
  195. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/maintaining-claude-md.md +0 -0
  196. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/maintaining-generated-types.md +0 -0
  197. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/providers.md +0 -0
  198. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/proxmox.md +0 -0
  199. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/remo-web-speckit-input.md +0 -0
  200. {remo_cli-4.0.0 → remo_cli-4.1.0}/docs/remo-web.html +0 -0
  201. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/index.html +0 -0
  202. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/package-lock.json +0 -0
  203. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/package.json +0 -0
  204. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/scripts/check-types-fresh.mjs +0 -0
  205. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/scripts/generate-frame-types.mjs +0 -0
  206. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/scripts/generate-schema-types.mjs +0 -0
  207. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/api/client.test.ts +0 -0
  208. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/api/client.ts +0 -0
  209. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/api/generated/README.md +0 -0
  210. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/api/generated/openapi.json +0 -0
  211. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/api/generated/schema.d.ts +0 -0
  212. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/api/generated/terminal-frames.d.ts +0 -0
  213. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/api/generated/terminal-frames.json +0 -0
  214. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/api/generated.test.ts +0 -0
  215. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/AppRoot.tsx +0 -0
  216. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/AppShell.css +0 -0
  217. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/AppShell.tsx +0 -0
  218. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/AwaitingAdoption.css +0 -0
  219. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/AwaitingAdoption.tsx +0 -0
  220. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/OfflineOverlay.css +0 -0
  221. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/OfflineOverlay.tsx +0 -0
  222. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/PairToSync.css +0 -0
  223. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/PairToSync.tsx +0 -0
  224. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/SessionRail.css +0 -0
  225. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/SessionRail.tsx +0 -0
  226. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/SettingsPage.css +0 -0
  227. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/SettingsPage.tsx +0 -0
  228. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/ShortcutsModal.css +0 -0
  229. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/ShortcutsModal.tsx +0 -0
  230. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/TopBar.css +0 -0
  231. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/TopBar.tsx +0 -0
  232. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/providerMeta.test.ts +0 -0
  233. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/providerMeta.ts +0 -0
  234. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/railModel.test.ts +0 -0
  235. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/components/railModel.ts +0 -0
  236. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/lib/clipboard.ts +0 -0
  237. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/lib/fullscreen.ts +0 -0
  238. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/main.tsx +0 -0
  239. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/discovery.test.ts +0 -0
  240. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/discovery.ts +0 -0
  241. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/fonts.ts +0 -0
  242. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/health.test.ts +0 -0
  243. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/health.ts +0 -0
  244. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/latency.test.ts +0 -0
  245. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/latency.ts +0 -0
  246. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/settings.test.ts +0 -0
  247. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/settings.ts +0 -0
  248. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/state/useConsoleKeyboard.ts +0 -0
  249. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/terminal/.gitkeep +0 -0
  250. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/terminal/RendererAdapter.ts +0 -0
  251. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/terminal/TerminalConnection.test.ts +0 -0
  252. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/terminal/TerminalConnection.ts +0 -0
  253. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/terminal/XtermRenderer.ts +0 -0
  254. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/terminal/defaultRenderer.ts +0 -0
  255. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/terminal/keymap.test.ts +0 -0
  256. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/terminal/keymap.ts +0 -0
  257. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/test/setup.ts +0 -0
  258. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/theme/fonts.ts +0 -0
  259. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/src/theme/tokens.css +0 -0
  260. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/tsconfig.json +0 -0
  261. {remo_cli-4.0.0 → remo_cli-4.1.0}/frontend/vite.config.ts +0 -0
  262. {remo_cli-4.0.0 → remo_cli-4.1.0}/install.sh +0 -0
  263. {remo_cli-4.0.0 → remo_cli-4.1.0}/maverick.yaml +0 -0
  264. {remo_cli-4.0.0 → remo_cli-4.1.0}/playwright.config.ts +0 -0
  265. {remo_cli-4.0.0 → remo_cli-4.1.0}/project-menu.sh +0 -0
  266. {remo_cli-4.0.0 → remo_cli-4.1.0}/release-please-config.json +0 -0
  267. {remo_cli-4.0.0 → remo_cli-4.1.0}/remo +0 -0
  268. {remo_cli-4.0.0 → remo_cli-4.1.0}/remo.svg +0 -0
  269. {remo_cli-4.0.0 → remo_cli-4.1.0}/run.sh +0 -0
  270. {remo_cli-4.0.0 → remo_cli-4.1.0}/scripts/export_openapi.py +0 -0
  271. {remo_cli-4.0.0 → remo_cli-4.1.0}/scripts/setup-aws-oidc.sh +0 -0
  272. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/checklists/requirements.md +0 -0
  273. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/checklists/validation.md +0 -0
  274. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/contracts/playbook-interface.yaml +0 -0
  275. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/contracts/role-interface.yaml +0 -0
  276. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/data-model.md +0 -0
  277. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/plan.md +0 -0
  278. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/quickstart.md +0 -0
  279. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/research.md +0 -0
  280. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/spec.md +0 -0
  281. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/001-bootstrap-incus-host/tasks.md +0 -0
  282. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/checklists/requirements.md +0 -0
  283. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/contracts/playbook-interface.yaml +0 -0
  284. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/contracts/role-interface.yaml +0 -0
  285. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/data-model.md +0 -0
  286. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/plan.md +0 -0
  287. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/quickstart.md +0 -0
  288. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/research.md +0 -0
  289. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/spec.md +0 -0
  290. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/002-incus-container-support/tasks.md +0 -0
  291. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/checklists/requirements.md +0 -0
  292. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/data-model.md +0 -0
  293. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/plan.md +0 -0
  294. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/quickstart.md +0 -0
  295. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/research.md +0 -0
  296. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/spec.md +0 -0
  297. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/003-python-cli-rewrite/tasks.md +0 -0
  298. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/004-proxmox-container-support/spec.md +0 -0
  299. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/005-provider-snapshots/checklists/requirements.md +0 -0
  300. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/005-provider-snapshots/contracts/cli-surface.md +0 -0
  301. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/005-provider-snapshots/data-model.md +0 -0
  302. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/005-provider-snapshots/plan.md +0 -0
  303. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/005-provider-snapshots/quickstart.md +0 -0
  304. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/005-provider-snapshots/research.md +0 -0
  305. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/005-provider-snapshots/spec.md +0 -0
  306. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/005-provider-snapshots/tasks.md +0 -0
  307. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/checklists/requirements.md +0 -0
  308. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/contracts/remo-host-protocol.md +0 -0
  309. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/contracts/rest-api.md +0 -0
  310. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/contracts/terminal-websocket.md +0 -0
  311. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/data-model.md +0 -0
  312. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/plan.md +0 -0
  313. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/quickstart.md +0 -0
  314. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/research.md +0 -0
  315. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/spec.md +0 -0
  316. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/010-web-session-interface/tasks.md +0 -0
  317. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/checklists/requirements.md +0 -0
  318. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/contracts/cli-web-adopt.md +0 -0
  319. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/contracts/setup-api.md +0 -0
  320. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/data-model.md +0 -0
  321. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/plan.md +0 -0
  322. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/quickstart-results.md +0 -0
  323. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/quickstart.md +0 -0
  324. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/research.md +0 -0
  325. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/spec.md +0 -0
  326. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/011-web-adopt/tasks.md +0 -0
  327. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/contracts/cli-web-adopt.md +0 -0
  328. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/contracts/pairing-api.md +0 -0
  329. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/contracts/setup-api.md +0 -0
  330. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/data-model.md +0 -0
  331. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/plan.md +0 -0
  332. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/quickstart.md +0 -0
  333. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/research.md +0 -0
  334. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/spec.md +0 -0
  335. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/012-web-adopt-pairing/tasks.md +0 -0
  336. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/013-managed-instance-tags/contracts/cli-sync.md +0 -0
  337. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/013-managed-instance-tags/contracts/marker-commands.md +0 -0
  338. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/013-managed-instance-tags/data-model.md +0 -0
  339. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/013-managed-instance-tags/plan.md +0 -0
  340. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/013-managed-instance-tags/quickstart.md +0 -0
  341. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/013-managed-instance-tags/research.md +0 -0
  342. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/013-managed-instance-tags/spec.md +0 -0
  343. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/013-managed-instance-tags/tasks.md +0 -0
  344. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/014-register-ssh-host/contracts/add-command.md +0 -0
  345. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/014-register-ssh-host/contracts/remove-command.md +0 -0
  346. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/014-register-ssh-host/data-model.md +0 -0
  347. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/014-register-ssh-host/plan.md +0 -0
  348. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/014-register-ssh-host/quickstart.md +0 -0
  349. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/014-register-ssh-host/research.md +0 -0
  350. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/014-register-ssh-host/spec.md +0 -0
  351. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/014-register-ssh-host/tasks.md +0 -0
  352. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/checklists/requirements.md +0 -0
  353. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/contracts/mirror-payload-v2.md +0 -0
  354. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/contracts/registry-accessor-api.md +0 -0
  355. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/contracts/registry-file-v2.md +0 -0
  356. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/data-model.md +0 -0
  357. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/plan.md +0 -0
  358. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/quickstart.md +0 -0
  359. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/research.md +0 -0
  360. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/spec.md +0 -0
  361. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/015-registry-v2/tasks.md +0 -0
  362. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/checklists/requirements.md +0 -0
  363. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/contracts/cli-sync.md +0 -0
  364. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/contracts/provider-probe.md +0 -0
  365. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/data-model.md +0 -0
  366. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/plan.md +0 -0
  367. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/quickstart.md +0 -0
  368. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/research.md +0 -0
  369. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/spec.md +0 -0
  370. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/016-sync-reconcile/tasks.md +0 -0
  371. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/checklists/requirements.md +0 -0
  372. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/contracts/cli-web-push.md +0 -0
  373. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/contracts/cli-web-status.md +0 -0
  374. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/contracts/revocation.md +0 -0
  375. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/contracts/setup-status-marker.md +0 -0
  376. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/data-model.md +0 -0
  377. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/plan.md +0 -0
  378. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/quickstart.md +0 -0
  379. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/research.md +0 -0
  380. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/spec.md +0 -0
  381. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/017-web-adopt-simplify/tasks.md +0 -0
  382. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/baseline-notes.md +0 -0
  383. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/checklists/requirements.md +0 -0
  384. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/cli-surface.md +0 -0
  385. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/errors.md +0 -0
  386. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/lifecycle-templates.md +0 -0
  387. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/provider-protocol.md +0 -0
  388. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/contracts/sync-merge.md +0 -0
  389. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/data-model.md +0 -0
  390. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/plan.md +0 -0
  391. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/quickstart.md +0 -0
  392. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/research.md +0 -0
  393. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/spec.md +0 -0
  394. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/018-provider-abstraction/tasks.md +0 -0
  395. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/checklists/requirements.md +0 -0
  396. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/contracts/cli-surface-delta.md +0 -0
  397. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/contracts/docs-structure-check.md +0 -0
  398. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/data-model.md +0 -0
  399. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/plan.md +0 -0
  400. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/quickstart.md +0 -0
  401. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/research.md +0 -0
  402. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/spec.md +0 -0
  403. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/019-hygiene-deps-docs/tasks.md +0 -0
  404. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/checklists/requirements.md +0 -0
  405. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/contracts/drift-checks.md +0 -0
  406. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/contracts/terminal-frames-v1.md +0 -0
  407. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/data-model.md +0 -0
  408. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/plan.md +0 -0
  409. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/quickstart.md +0 -0
  410. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/research.md +0 -0
  411. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/spec.md +0 -0
  412. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/020-openapi-type-generation/tasks.md +0 -0
  413. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/021-cli-plane-separation/contracts/cli-surface.md +0 -0
  414. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/021-cli-plane-separation/contracts/descriptor-schema.md +0 -0
  415. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/021-cli-plane-separation/data-model.md +0 -0
  416. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/021-cli-plane-separation/plan.md +0 -0
  417. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/021-cli-plane-separation/quickstart.md +0 -0
  418. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/021-cli-plane-separation/research.md +0 -0
  419. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/021-cli-plane-separation/spec.md +0 -0
  420. {remo_cli-4.0.0 → remo_cli-4.1.0}/specs/021-cli-plane-separation/tasks.md +0 -0
  421. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/__init__.py +0 -0
  422. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/__main__.py +0 -0
  423. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/cli/__init__.py +0 -0
  424. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/cli/added.py +0 -0
  425. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/cli/cp.py +0 -0
  426. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/cli/main.py +0 -0
  427. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/cli/providers/__init__.py +0 -0
  428. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/cli/providers/factory.py +0 -0
  429. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/cli/shell.py +0 -0
  430. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/cli/web.py +0 -0
  431. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/__init__.py +0 -0
  432. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/ansible_runner.py +0 -0
  433. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/completion.py +0 -0
  434. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/config.py +0 -0
  435. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/errors.py +0 -0
  436. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/known_hosts.py +0 -0
  437. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/lifecycle.py +0 -0
  438. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/output.py +0 -0
  439. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/picker.py +0 -0
  440. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/provider_protocol.py +0 -0
  441. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/provider_registry.py +0 -0
  442. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/reconcile.py +0 -0
  443. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/registry.py +0 -0
  444. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/remo_host_client.py +0 -0
  445. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/rsync.py +0 -0
  446. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/snapshot.py +0 -0
  447. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/ssh.py +0 -0
  448. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/validation.py +0 -0
  449. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/version.py +0 -0
  450. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/web_adopt.py +0 -0
  451. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/core/web_drift.py +0 -0
  452. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/models/__init__.py +0 -0
  453. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/models/capability.py +0 -0
  454. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/models/discovery.py +0 -0
  455. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/models/host.py +0 -0
  456. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/models/session_target.py +0 -0
  457. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/models/snapshot.py +0 -0
  458. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/__init__.py +0 -0
  459. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/added.py +0 -0
  460. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/aws.py +0 -0
  461. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/aws_descriptor.py +0 -0
  462. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/builtin.py +0 -0
  463. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/hetzner.py +0 -0
  464. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/hetzner_descriptor.py +0 -0
  465. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/incus.py +0 -0
  466. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/incus_descriptor.py +0 -0
  467. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/proxmox.py +0 -0
  468. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/providers/proxmox_descriptor.py +0 -0
  469. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/__init__.py +0 -0
  470. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/api/__init__.py +0 -0
  471. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/api/hosts.py +0 -0
  472. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/api/pairing.py +0 -0
  473. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/api/setup.py +0 -0
  474. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/api/terminals.py +0 -0
  475. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/app.py +0 -0
  476. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/check.py +0 -0
  477. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/config.py +0 -0
  478. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/discovery.py +0 -0
  479. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/frames.py +0 -0
  480. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/health.py +0 -0
  481. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/logging_config.py +0 -0
  482. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/models.py +0 -0
  483. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/operator_auth.py +0 -0
  484. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/pairing.py +0 -0
  485. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/ssh_master.py +0 -0
  486. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/state.py +0 -0
  487. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/terminal.py +0 -0
  488. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/terminal_registry.py +0 -0
  489. {remo_cli-4.0.0 → remo_cli-4.1.0}/src/remo_cli/web/tokens.py +0 -0
  490. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/__init__.py +0 -0
  491. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/ansible/__init__.py +0 -0
  492. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/ansible/test_apt_sources_repair.py +0 -0
  493. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/ansible/test_authorized_keys_bootstrap.py +0 -0
  494. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/ansible/test_remo_host_idempotency.py +0 -0
  495. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/conftest.py +0 -0
  496. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/e2e/fixtures.ts +0 -0
  497. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/e2e/keyboard-routing.spec.ts +0 -0
  498. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/e2e/mobile.spec.ts +0 -0
  499. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/e2e/reconnect.spec.ts +0 -0
  500. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/e2e/terminal-shell-io.spec.ts +0 -0
  501. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/e2e/terminal-tui-compatibility.spec.ts +0 -0
  502. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/e2e/workspace-layout.spec.ts +0 -0
  503. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/image/__init__.py +0 -0
  504. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/image/test_docker_image.py +0 -0
  505. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/__init__.py +0 -0
  506. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/_nine_terminal_fixture.py +0 -0
  507. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/fish_completion.sh +0 -0
  508. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/orbstack-cloud-init.yml +0 -0
  509. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/orbstack.sh +0 -0
  510. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_nine_terminals.py +0 -0
  511. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_nine_terminals_soak.py +0 -0
  512. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_registry_concurrency.py +0 -0
  513. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_remo_host_e2e.py +0 -0
  514. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_security_rejections.py +0 -0
  515. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_setup_payload_versions.py +0 -0
  516. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_sync_reconcile.py +0 -0
  517. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_terminal_attach.py +0 -0
  518. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_web_adopt_e2e.py +0 -0
  519. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/integration/test_web_cli_parity.py +0 -0
  520. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/perf/__init__.py +0 -0
  521. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/perf/test_latency.py +0 -0
  522. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/perf/test_registry_perf.py +0 -0
  523. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/__init__.py +0 -0
  524. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/__init__.py +0 -0
  525. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/providers/test_aws_snapshot.py +0 -0
  526. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/providers/test_hetzner_snapshot.py +0 -0
  527. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/providers/test_incus_snapshot.py +0 -0
  528. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/providers/test_incus_sync_all.py +0 -0
  529. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/providers/test_proxmox_snapshot.py +0 -0
  530. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/providers/test_proxmox_sync_all.py +0 -0
  531. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/surface_baseline.py +0 -0
  532. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_add_cmd.py +0 -0
  533. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_cli_uniformity.py +0 -0
  534. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_cp.py +0 -0
  535. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_main.py +0 -0
  536. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_remove_cmd.py +0 -0
  537. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_shell.py +0 -0
  538. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_shell_added.py +0 -0
  539. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_startup_imports.py +0 -0
  540. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_surface_preservation.py +0 -0
  541. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/cli/test_web_adopt_cmd.py +0 -0
  542. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/__init__.py +0 -0
  543. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_ansible_runner.py +0 -0
  544. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_completion.py +0 -0
  545. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_config.py +0 -0
  546. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_errors.py +0 -0
  547. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_known_hosts.py +0 -0
  548. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_lifecycle.py +0 -0
  549. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_migration_tagging_notice.py +0 -0
  550. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_output.py +0 -0
  551. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_picker.py +0 -0
  552. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_provider_registry.py +0 -0
  553. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_reconcile.py +0 -0
  554. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_registry_format.py +0 -0
  555. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_registry_locking.py +0 -0
  556. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_registry_migration.py +0 -0
  557. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_remo_host_client.py +0 -0
  558. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_rsync.py +0 -0
  559. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_snapshot.py +0 -0
  560. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_ssh.py +0 -0
  561. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_ssh_added.py +0 -0
  562. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_ssh_controlpath.py +0 -0
  563. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_ssh_identity_opts.py +0 -0
  564. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_validation.py +0 -0
  565. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_version.py +0 -0
  566. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_web_adopt_authorize.py +0 -0
  567. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_web_adopt_code.py +0 -0
  568. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_web_adopt_payload.py +0 -0
  569. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_web_adopt_trust.py +0 -0
  570. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_web_drift.py +0 -0
  571. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_web_push.py +0 -0
  572. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/core/test_web_push_self_heal.py +0 -0
  573. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/models/__init__.py +0 -0
  574. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/models/test_capability.py +0 -0
  575. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/models/test_discovery.py +0 -0
  576. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/models/test_session_target.py +0 -0
  577. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/__init__.py +0 -0
  578. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/fake_provider.py +0 -0
  579. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_added_add.py +0 -0
  580. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_added_provider_guard.py +0 -0
  581. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_added_update_remove.py +0 -0
  582. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_added_verify.py +0 -0
  583. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_aws_provider_protocol.py +0 -0
  584. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_aws_snapshot.py +0 -0
  585. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_aws_sync.py +0 -0
  586. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_aws_upgrade_resize.py +0 -0
  587. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_entry_adapters.py +0 -0
  588. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_http.py +0 -0
  589. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_label.py +0 -0
  590. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_resize.py +0 -0
  591. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_snapshot.py +0 -0
  592. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_hetzner_sync.py +0 -0
  593. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_host_user_consistency.py +0 -0
  594. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_incus_entry_adapters.py +0 -0
  595. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_incus_marker.py +0 -0
  596. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_incus_resize.py +0 -0
  597. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_incus_snapshot.py +0 -0
  598. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_incus_sync.py +0 -0
  599. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_missing_sdk.py +0 -0
  600. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_provider_conformance.py +0 -0
  601. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_provider_registry_entries.py +0 -0
  602. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_devcontainer_runtime.py +0 -0
  603. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_marker.py +0 -0
  604. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_provider_protocol.py +0 -0
  605. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_resize.py +0 -0
  606. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_snapshot.py +0 -0
  607. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/providers/test_proxmox_sync.py +0 -0
  608. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/test_ansible_templates.py +0 -0
  609. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/test_architecture.py +0 -0
  610. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/test_docs_structure.py +0 -0
  611. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/test_host_model.py +0 -0
  612. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/test_host_ssh_type.py +0 -0
  613. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/test_lockfile_version.py +0 -0
  614. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/test_schema_drift.py +0 -0
  615. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/__init__.py +0 -0
  616. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/_pairing_support.py +0 -0
  617. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/conftest.py +0 -0
  618. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/fixtures/hosts_response.json +0 -0
  619. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/fixtures/sessions_response.json +0 -0
  620. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_backpressure.py +0 -0
  621. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_check.py +0 -0
  622. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_csp.py +0 -0
  623. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_discovery_refresh_force.py +0 -0
  624. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_frames.py +0 -0
  625. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_health_states.py +0 -0
  626. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_hosts_mapping.py +0 -0
  627. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_lazy_import.py +0 -0
  628. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_log_redaction.py +0 -0
  629. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_mint_gating.py +0 -0
  630. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_operator_auth.py +0 -0
  631. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_pairing.py +0 -0
  632. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_pairing_origin.py +0 -0
  633. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_payload_equivalence.py +0 -0
  634. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_registry_readonly.py +0 -0
  635. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_setup_api.py +0 -0
  636. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_setup_auth.py +0 -0
  637. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_setup_dormancy.py +0 -0
  638. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_state.py +0 -0
  639. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_terminal_resize.py +0 -0
  640. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_terminals_api.py +0 -0
  641. {remo_cli-4.0.0 → remo_cli-4.1.0}/tests/unit/web/test_tokens.py +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "4.1.0"
3
+ }
@@ -117,6 +117,7 @@ frontend/ # remo-web browser SPA (Vite + React + TypeScript)
117
117
  ├── src/
118
118
  │ ├── api/client.ts # REST + WS terminal client (remo-terminal.v1 subprotocol)
119
119
  │ ├── components/ # Dashboard, InstanceGroup, TargetCard, GridView, TabView, TerminalCard
120
+ │ │ # + masterLayout.ts (pure pane geometry: uniform grid + master/stack tiling)
120
121
  │ ├── state/ # discovery.ts, workspace.ts (layout persisted to localStorage)
121
122
  │ └── terminal/ # RendererAdapter (seam), XtermRenderer (the one engine), TerminalConnection, keymap
122
123
 
@@ -135,7 +136,8 @@ ansible/ # Ansible playbooks (invoked by Python via subprocess
135
136
  └── requirements.yml
136
137
 
137
138
  scripts/ # Repo-root utility scripts (not part of the installed package)
138
- └── export_openapi.py # Exports frontend/src/api/generated/{openapi.json,terminal-frames.json} (feature 020)
139
+ ├── export_openapi.py # Exports frontend/src/api/generated/{openapi.json,terminal-frames.json} (feature 020)
140
+ └── palette-check.sh # Prints every ANSI colour x normal/bold/dim/bright in a live terminal — audits a theme's legibility where contrast maths can't
139
141
 
140
142
  pyproject.toml # Build config, dependencies (incl. `web` extra), console_scripts entry point
141
143
  ```
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.1.0](https://github.com/get2knowio/remo/compare/v4.0.1...v4.1.0) (2026-08-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * **web:** tile a terminal to a pane edge as a master area ([#137](https://github.com/get2knowio/remo/issues/137)) ([41802fb](https://github.com/get2knowio/remo/commit/41802fb17ae5741918a31ee9d662aa90808ebb9a))
9
+
10
+ ## [4.0.1](https://github.com/get2knowio/remo/compare/v4.0.0...v4.0.1) (2026-08-03)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **web:** let the terminal-theme menu scroll instead of being clipped ([#134](https://github.com/get2knowio/remo/issues/134)) ([ffb34e7](https://github.com/get2knowio/remo/commit/ffb34e72cc9d1744130130934538d38af217ec73))
16
+ * **web:** make Remo Light's white family visible — `white` and `brightWhite` were 1.27:1 and 1.09:1 against the background, hiding dim and bold text ([#134](https://github.com/get2knowio/remo/issues/134)) ([c5bc07a](https://github.com/get2knowio/remo/commit/c5bc07a7))
17
+
3
18
  ## [4.0.0](https://github.com/get2knowio/remo/compare/v3.1.0...v4.0.0) (2026-08-03)
4
19
 
5
20
 
@@ -117,6 +117,7 @@ frontend/ # remo-web browser SPA (Vite + React + TypeScript)
117
117
  ├── src/
118
118
  │ ├── api/client.ts # REST + WS terminal client (remo-terminal.v1 subprotocol)
119
119
  │ ├── components/ # Dashboard, InstanceGroup, TargetCard, GridView, TabView, TerminalCard
120
+ │ │ # + masterLayout.ts (pure pane geometry: uniform grid + master/stack tiling)
120
121
  │ ├── state/ # discovery.ts, workspace.ts (layout persisted to localStorage), settings.ts (display prefs: site light/dark mode, accent, fonts, terminal theme + per-target overrides)
121
122
  │ ├── terminal/ # RendererAdapter (seam), XtermRenderer (the one engine), TerminalConnection, keymap
122
123
  │ └── theme/ # tokens.css (light-dark() palette, one pair per token), fonts.ts, terminalThemes.ts (8 terminal color schemes: a token-derived Remo Dark/Light pair the default 'auto' selection tracks, plus 6 curated third-party)
@@ -136,7 +137,8 @@ ansible/ # Ansible playbooks (invoked by Python via subprocess
136
137
  └── requirements.yml
137
138
 
138
139
  scripts/ # Repo-root utility scripts (not part of the installed package)
139
- └── export_openapi.py # Exports frontend/src/api/generated/{openapi.json,terminal-frames.json} (feature 020)
140
+ ├── export_openapi.py # Exports frontend/src/api/generated/{openapi.json,terminal-frames.json} (feature 020)
141
+ └── palette-check.sh # Prints every ANSI colour x normal/bold/dim/bright in a live terminal — audits a theme's legibility where contrast maths can't
140
142
 
141
143
  pyproject.toml # Build config, dependencies (incl. `web` extra), console_scripts entry point
142
144
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remo-cli
3
- Version: 4.0.0
3
+ Version: 4.1.0
4
4
  Summary: Remote development environment CLI
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -108,7 +108,16 @@ The SPA is a two-pane **web console**:
108
108
  row (or its `+` button) **adds** it to a responsive grid (1/2/3 columns by count). In a grid, **drag
109
109
  a tile's header onto another to swap their positions** — a window outline follows the cursor and the
110
110
  swap target shows a dashed outline (mouse or touch press-and-hold; keyboard-accessible via the
111
- handle); the arrangement persists until the grid is rebuilt. In a grid, **resting the pointer on a
111
+ handle); the arrangement persists until the grid is rebuilt. **Drag a tile to a pane edge instead of
112
+ onto another tile** and it takes that whole side as a *master area* — full height on the left/right,
113
+ full width on the top/bottom — with the remaining tiles tiling the leftover strip (the dwm/xmonad
114
+ master+stack model). A translucent band marks each edge while dragging and fills in when the drop
115
+ would land there. The **▦** control in a tile's header cycles the same thing without a drag
116
+ (▌ left → ▀ top → ▐ right → ▄ bottom → back to the even grid), which is the practical path on a touch
117
+ device. The master takes 60% by default, closing it promotes the head of the stack rather than
118
+ un-tiling everything, and the arrangement persists across reload alongside the tile order. This is
119
+ what lets three terminals fill the pane without the empty fourth cell a 2x2 grid would leave. In a
120
+ grid, **resting the pointer on a
112
121
  tile focuses it** (focus-follows-mouse, with a short dwell so passing through tiles doesn't steal
113
122
  focus) — keystrokes go where the pointer is, no click needed. The **◻** control solos a tile; **Esc**
114
123
  collapses the grid back to the focused terminal;
@@ -228,16 +228,23 @@
228
228
  border-color: var(--accent-strong);
229
229
  }
230
230
 
231
+ /* Positioned against the viewport from JS (themeMenuPosition) — `position`,
232
+ * `top`/`bottom`, `left` and `max-height` all arrive as inline styles. It has
233
+ * to escape `.terminal-card`'s `overflow: hidden`, which used to clip the list
234
+ * inside a short grid tile with no way to scroll to the rest. */
231
235
  .tc-theme-menu {
232
- position: absolute;
233
- top: calc(100% + 6px);
234
- right: 0;
235
- z-index: 20;
236
+ position: fixed;
237
+ z-index: 60;
236
238
  min-width: 216px;
239
+ max-width: calc(100vw - 16px);
237
240
  padding: 5px;
238
241
  display: flex;
239
242
  flex-direction: column;
240
243
  gap: 2px;
244
+ overflow-y: auto;
245
+ /* Stop at the menu's own edges instead of scrolling the page behind it —
246
+ * the whole reported symptom was a swipe here moving the whole console. */
247
+ overscroll-behavior: contain;
241
248
  background: var(--bg-elev);
242
249
  border: 1px solid var(--border-strong);
243
250
  border-radius: 10px;
@@ -7,6 +7,7 @@ import { act, render, screen } from "@testing-library/react";
7
7
  import { DndContext } from "@dnd-kit/core";
8
8
  import { beforeEach, describe, expect, it, vi } from "vitest";
9
9
  import type { SessionTarget } from "../api/client";
10
+ import { themeMenuPosition } from "./TerminalCard";
10
11
  import type { RendererAdapter } from "../terminal/RendererAdapter";
11
12
 
12
13
  const adapters: RendererAdapter[] = [];
@@ -196,3 +197,59 @@ describe("TerminalCard terminal theme", () => {
196
197
  expect(screen.queryByRole("menu")).not.toBeInTheDocument();
197
198
  });
198
199
  });
200
+
201
+ // The reported bug: in a 2x2 grid on a small display the theme list was cut off
202
+ // at the card's edge (`.terminal-card` sets `overflow: hidden`) and swiping it
203
+ // scrolled the whole page instead. The menu is now placed against the viewport
204
+ // with a computed max-height, which is what gives it something to scroll.
205
+ describe("themeMenuPosition", () => {
206
+ const VIEWPORT = { width: 1024, height: 768 };
207
+
208
+ it("opens below the swatch when there is room, right-aligned to it", () => {
209
+ const pos = themeMenuPosition({ top: 100, bottom: 126, right: 500 }, VIEWPORT);
210
+ expect(pos.placement).toBe("below");
211
+ expect(pos.style).toMatchObject({ position: "fixed", top: 132, left: 500 - 216 });
212
+ });
213
+
214
+ it("caps the height to the space available, so the list scrolls instead of overflowing", () => {
215
+ // Room below is 768 - 560 - 6 - 8 = 194: enough to stay below, but well
216
+ // short of the ~300px the nine-entry list wants. That gap is the scroll.
217
+ const pos = themeMenuPosition({ top: 534, bottom: 560, right: 500 }, VIEWPORT);
218
+ expect(pos.placement).toBe("below");
219
+ expect(pos.style.maxHeight).toBe(194);
220
+ });
221
+
222
+ it("floors the height so a cramped anchor still shows a usable list", () => {
223
+ // Below is only 154 here, and above (560) is roomier — so it flips, rather
224
+ // than rendering a sliver.
225
+ const pos = themeMenuPosition({ top: 574, bottom: 600, right: 500 }, VIEWPORT);
226
+ expect(pos.placement).toBe("above");
227
+ expect(pos.style.maxHeight).toBe(560);
228
+ });
229
+
230
+ it("flips above the swatch when below is too cramped and above is roomier", () => {
231
+ const pos = themeMenuPosition({ top: 700, bottom: 726, right: 500 }, VIEWPORT);
232
+ expect(pos.placement).toBe("above");
233
+ // Anchored by its bottom edge, so it grows upward without needing its height.
234
+ expect(pos.style.bottom).toBe(768 - 700 + 6);
235
+ expect(pos.style.top).toBeUndefined();
236
+ });
237
+
238
+ it("never places the menu off the right edge", () => {
239
+ const pos = themeMenuPosition({ top: 40, bottom: 66, right: 1020 }, VIEWPORT);
240
+ expect(pos.style.left).toBe(1024 - 216 - 8);
241
+ });
242
+
243
+ it("never places the menu off the left edge on a narrow screen", () => {
244
+ const pos = themeMenuPosition({ top: 40, bottom: 66, right: 120 }, { width: 380, height: 800 });
245
+ expect(pos.style.left).toBe(8);
246
+ });
247
+
248
+ // A card mid-teardown (or jsdom, which reports zeroes) must not produce a
249
+ // negative max-height and an unusable sliver of a menu.
250
+ it("stays usable for a degenerate anchor", () => {
251
+ const pos = themeMenuPosition({ top: 0, bottom: 0, right: 0 }, { width: 0, height: 0 });
252
+ expect(pos.style.maxHeight as number).toBeGreaterThan(0);
253
+ expect(pos.style.left as number).toBeGreaterThanOrEqual(0);
254
+ });
255
+ });
@@ -9,7 +9,14 @@
9
9
  // single↔grid only re-renders the header chrome and never remounts the
10
10
  // terminal surface (which would tear down the live connection).
11
11
 
12
- import { useCallback, useEffect, useRef, useState, type CSSProperties } from "react";
12
+ import {
13
+ useCallback,
14
+ useEffect,
15
+ useLayoutEffect,
16
+ useRef,
17
+ useState,
18
+ type CSSProperties,
19
+ } from "react";
13
20
  import { useDraggable, useDroppable } from "@dnd-kit/core";
14
21
  import type { SessionTarget, TypedError } from "../api/client";
15
22
  import { providerMeta } from "./providerMeta";
@@ -23,6 +30,7 @@ import {
23
30
  type TerminalFontOptions,
24
31
  } from "../state/settings";
25
32
  import { TERMINAL_THEMES, type TerminalThemeColors } from "../theme/terminalThemes";
33
+ import type { MasterSide } from "../state/workspace";
26
34
  import { removeLatency, reportLatency } from "../state/latency";
27
35
  import type { RendererAdapter } from "../terminal/RendererAdapter";
28
36
  import { createDefaultRenderer } from "../terminal/defaultRenderer";
@@ -34,6 +42,16 @@ const DEFAULT_ROWS = 24;
34
42
  /** How much to shrink the terminal font in a grid tile when "scale to fit". */
35
43
  const GRID_FIT_SCALE = 0.8;
36
44
 
45
+ /** Glyph + label per tiling state. The glyph is a half block drawing where the
46
+ * master area sits, so the button reads as a picture of the layout. */
47
+ const MASTER_GLYPH: Record<MasterSide | "grid", { glyph: string; title: string }> = {
48
+ grid: { glyph: "▦", title: "Tile this terminal to the left" },
49
+ left: { glyph: "▌", title: "Mastering the left — tile to the top" },
50
+ top: { glyph: "▀", title: "Mastering the top — tile to the right" },
51
+ right: { glyph: "▐", title: "Mastering the right — tile to the bottom" },
52
+ bottom: { glyph: "▄", title: "Mastering the bottom — back to the even grid" },
53
+ };
54
+
37
55
  const STATE_LABELS: Record<TerminalConnectionState, string> = {
38
56
  connecting: "Connecting…",
39
57
  ready: "Connected",
@@ -63,6 +81,16 @@ interface TerminalCardProps {
63
81
  /** When true, the header acts as a dnd-kit drag handle and the tile is a drop
64
82
  * target (grid reorder). The DndContext + swap live in WorkspacePane. */
65
83
  reorderEnabled?: boolean;
84
+ /** This tile's slot in a master/stack tiling, as a CSS `grid-area`. Undefined
85
+ * in the uniform grid, where tiles auto-place exactly as they always have.
86
+ * Applied to the card's OWN root element — never a wrapper, see WorkspacePane. */
87
+ gridArea?: string;
88
+ /** Which side this tile masters, or null when it is a plain stack tile.
89
+ * Undefined hides the tiling control entirely (single view, fullscreen). */
90
+ masterSide?: MasterSide | null;
91
+ /** Advance this tile through the tiling cycle (left -> top -> right -> bottom
92
+ * -> uniform grid). */
93
+ onCycleTile?: () => void;
66
94
  /** Window-control "Normal": solo this terminal into the single view. */
67
95
  onNormal: () => void;
68
96
  /** Window-control "Grid": show the grid — omitted when none is available. */
@@ -84,6 +112,71 @@ interface TerminalCardProps {
84
112
  onStarted?: () => void;
85
113
  }
86
114
 
115
+ /** Where the terminal-theme popup goes, as viewport-relative inline styles. */
116
+ export interface ThemeMenuPosition {
117
+ style: CSSProperties;
118
+ /** Which side of the swatch the menu opened on (asserted in tests). */
119
+ placement: "below" | "above";
120
+ }
121
+
122
+ /** Gap between the swatch and the menu, and the minimum breathing room kept
123
+ * against the viewport edges. */
124
+ const THEME_MENU_GAP = 6;
125
+ const THEME_MENU_MARGIN = 8;
126
+ /** Matches the menu's min-width in TerminalCard.css. */
127
+ const THEME_MENU_WIDTH = 216;
128
+ /** Below this, "below the swatch" is too cramped to be worth using. */
129
+ const THEME_MENU_MIN_USABLE = 160;
130
+
131
+ /**
132
+ * Place the theme popup against the VIEWPORT rather than the card.
133
+ *
134
+ * A grid tile on a small screen (a 2x2 layout on an iPad, say) is routinely
135
+ * shorter than the nine-entry theme list, and `.terminal-card` sets
136
+ * `overflow: hidden` — so an absolutely-positioned menu was silently clipped at
137
+ * the card's edge with no way to reach the rest. Fixed positioning escapes that
138
+ * clip, and the computed `maxHeight` is what gives the menu something to scroll
139
+ * (paired with `overscroll-behavior: contain` in CSS, so the scroll stops at the
140
+ * menu instead of running the page).
141
+ *
142
+ * Opens downward by default, flipping above the swatch when there is more room
143
+ * there, and clamps horizontally so the menu can never sit off-screen.
144
+ */
145
+ export function themeMenuPosition(
146
+ anchor: { top: number; bottom: number; right: number },
147
+ viewport: { width: number; height: number } = {
148
+ width: window.innerWidth,
149
+ height: window.innerHeight,
150
+ },
151
+ ): ThemeMenuPosition {
152
+ const roomBelow = viewport.height - anchor.bottom - THEME_MENU_GAP - THEME_MENU_MARGIN;
153
+ const roomAbove = anchor.top - THEME_MENU_GAP - THEME_MENU_MARGIN;
154
+ const placement: "below" | "above" =
155
+ roomBelow < THEME_MENU_MIN_USABLE && roomAbove > roomBelow ? "above" : "below";
156
+
157
+ // Right-align to the swatch, then clamp both edges into the viewport.
158
+ const left = Math.max(
159
+ THEME_MENU_MARGIN,
160
+ Math.min(anchor.right - THEME_MENU_WIDTH, viewport.width - THEME_MENU_WIDTH - THEME_MENU_MARGIN),
161
+ );
162
+ // Never negative: a zero-height anchor (jsdom, or a card mid-teardown) would
163
+ // otherwise ask for a negative max-height and render an unusable sliver.
164
+ const maxHeight = Math.max(THEME_MENU_MIN_USABLE, placement === "above" ? roomAbove : roomBelow);
165
+
166
+ return {
167
+ placement,
168
+ style:
169
+ placement === "above"
170
+ ? {
171
+ position: "fixed",
172
+ bottom: viewport.height - anchor.top + THEME_MENU_GAP,
173
+ left,
174
+ maxHeight,
175
+ }
176
+ : { position: "fixed", top: anchor.bottom + THEME_MENU_GAP, left, maxHeight },
177
+ };
178
+ }
179
+
87
180
  function effectiveFont(settings: SettingsState, mode: TerminalCardMode): TerminalFontOptions {
88
181
  const base = terminalFontOptions(settings);
89
182
  if (mode === "grid" && settings.gridFit) {
@@ -101,6 +194,9 @@ export function TerminalCard({
101
194
  viewState,
102
195
  onClose,
103
196
  reorderEnabled,
197
+ gridArea,
198
+ masterSide,
199
+ onCycleTile,
104
200
  onNormal,
105
201
  onGrid,
106
202
  onToggleFullscreen,
@@ -142,6 +238,9 @@ export function TerminalCard({
142
238
  const [copied, setCopied] = useState(false);
143
239
  const [themeMenuOpen, setThemeMenuOpen] = useState(false);
144
240
  const themeMenuRef = useRef<HTMLDivElement | null>(null);
241
+ const themeButtonRef = useRef<HTMLButtonElement | null>(null);
242
+ // Viewport-relative placement for the theme popup; null until measured.
243
+ const [themeMenuPos, setThemeMenuPos] = useState<ThemeMenuPosition | null>(null);
145
244
 
146
245
  // dnd-kit reorder: the tile is both a draggable (handle = header grip, below)
147
246
  // and a droppable (swap target). Disabled outside a reorderable grid. We use
@@ -336,6 +435,39 @@ export function TerminalCard({
336
435
  }
337
436
  }, [isFocused, isVisible]);
338
437
 
438
+ // Measure the popup's placement before paint, and keep it anchored while it
439
+ // is open. A grid tile can be far smaller than the list, so the menu is
440
+ // positioned against the VIEWPORT (see ThemeMenuPosition) rather than the
441
+ // card, which clips its overflow.
442
+ useLayoutEffect(() => {
443
+ if (!themeMenuOpen) {
444
+ setThemeMenuPos(null);
445
+ return undefined;
446
+ }
447
+ const measure = (): void => {
448
+ const button = themeButtonRef.current;
449
+ if (button) {
450
+ setThemeMenuPos(themeMenuPosition(button.getBoundingClientRect()));
451
+ }
452
+ };
453
+ measure();
454
+ // A rotate/resize moves the anchor; so does scrolling the pane under it.
455
+ // Scrolling INSIDE the menu must not re-anchor (it would fight the user),
456
+ // and element scrolls only reach window listeners in the capture phase.
457
+ const onScroll = (e: Event): void => {
458
+ if (themeMenuRef.current?.contains(e.target as Node)) {
459
+ return;
460
+ }
461
+ measure();
462
+ };
463
+ window.addEventListener("resize", measure);
464
+ window.addEventListener("scroll", onScroll, true);
465
+ return () => {
466
+ window.removeEventListener("resize", measure);
467
+ window.removeEventListener("scroll", onScroll, true);
468
+ };
469
+ }, [themeMenuOpen]);
470
+
339
471
  // Dismiss the theme popup on Escape or a click anywhere outside it.
340
472
  useEffect(() => {
341
473
  if (!themeMenuOpen) {
@@ -436,6 +568,7 @@ export function TerminalCard({
436
568
  style={
437
569
  {
438
570
  display: isVisible ? undefined : "none",
571
+ gridArea,
439
572
  "--bg-term": theme.colors.background,
440
573
  } as CSSProperties
441
574
  }
@@ -501,6 +634,7 @@ export function TerminalCard({
501
634
  <div className="tc-theme" ref={themeMenuRef}>
502
635
  <button
503
636
  type="button"
637
+ ref={themeButtonRef}
504
638
  className="tc-btn tc-btn--swatch"
505
639
  data-testid={`terminal-theme-${target.id}`}
506
640
  title={`Terminal theme: ${theme.label}${hasThemeOverride ? "" : " (default)"}`}
@@ -515,8 +649,8 @@ export function TerminalCard({
515
649
  >
516
650
  A
517
651
  </button>
518
- {themeMenuOpen && (
519
- <div className="tc-theme-menu" role="menu">
652
+ {themeMenuOpen && themeMenuPos && (
653
+ <div className="tc-theme-menu" role="menu" style={themeMenuPos.style}>
520
654
  <button
521
655
  type="button"
522
656
  className={`tc-theme-item${hasThemeOverride ? "" : " tc-theme-item--on"}`}
@@ -563,6 +697,26 @@ export function TerminalCard({
563
697
  </div>
564
698
  )}
565
699
  </div>
700
+ {/* Tiling control. Cycles rather than opening a menu: it is one tap on
701
+ * a touch device, where dragging to a 64px edge band with a thumb is
702
+ * the awkward path. The half-block glyphs draw where the master area
703
+ * lands, so the current state is legible without the tooltip. */}
704
+ {onCycleTile && (
705
+ <button
706
+ type="button"
707
+ className={`tc-btn tc-btn--icon${masterSide ? " tc-btn--active" : ""}`}
708
+ data-testid={`terminal-tile-${target.id}`}
709
+ title={MASTER_GLYPH[masterSide ?? "grid"].title}
710
+ aria-label={MASTER_GLYPH[masterSide ?? "grid"].title}
711
+ aria-pressed={Boolean(masterSide)}
712
+ onClick={(e) => {
713
+ e.stopPropagation();
714
+ onCycleTile();
715
+ }}
716
+ >
717
+ {MASTER_GLYPH[masterSide ?? "grid"].glyph}
718
+ </button>
719
+ )}
566
720
  {/* Window-control cluster: mutually-exclusive display modes ordered
567
721
  * by how much space they take — Grid (smaller/tiled) → Normal (fills
568
722
  * the app's main pane) → Fullscreen (whole window) — plus close. The
@@ -0,0 +1,139 @@
1
+ .workspace {
2
+ flex: 1;
3
+ min-width: 0;
4
+ min-height: 0;
5
+ display: flex;
6
+ flex-direction: column;
7
+ background: var(--bg-pane);
8
+ }
9
+
10
+ .workspace-body {
11
+ flex: 1;
12
+ min-height: 0;
13
+ padding: 12px;
14
+ gap: 10px;
15
+ }
16
+
17
+ .workspace-body--single {
18
+ display: flex;
19
+ flex-direction: column;
20
+ }
21
+
22
+ .workspace-body--grid {
23
+ display: grid;
24
+ }
25
+
26
+ /* Containing block for the snap zones below, and a stop for touch scroll
27
+ * chaining while dragging a tile around. */
28
+ .workspace-body {
29
+ position: relative;
30
+ overscroll-behavior: contain;
31
+ }
32
+
33
+ /* --- edge drop targets (master/stack tiling) ---
34
+ *
35
+ * `pointer-events: none` is load-bearing, not a tidy-up: dnd-kit resolves drops
36
+ * from measured rects rather than hit-testing, so these can blanket the pane
37
+ * edges without intercepting anything — including the rail's resize handle,
38
+ * which overhangs 3px into this pane's left edge.
39
+ *
40
+ * z-index 20 sits in the free band between the rail divider (8) and the terminal
41
+ * theme popup (60). */
42
+ .workspace-snap {
43
+ position: absolute;
44
+ z-index: 20;
45
+ pointer-events: none;
46
+ opacity: 0;
47
+ border-radius: var(--radius-lg);
48
+ border: 2px dashed transparent;
49
+ background: color-mix(in oklch, var(--accent) 14%, transparent);
50
+ }
51
+ @media (prefers-reduced-motion: no-preference) {
52
+ .workspace-snap {
53
+ transition: opacity 120ms ease;
54
+ }
55
+ }
56
+ /* Visible but quiet while a drag is in flight, solid once it would land here. */
57
+ .workspace-snap--armed {
58
+ opacity: 0.4;
59
+ }
60
+ .workspace-snap--over {
61
+ opacity: 1;
62
+ border-color: var(--accent-strong);
63
+ background: color-mix(in oklch, var(--accent) 28%, transparent);
64
+ }
65
+
66
+ .workspace-snap--left,
67
+ .workspace-snap--right {
68
+ top: 0;
69
+ bottom: 0;
70
+ width: min(64px, 18%);
71
+ }
72
+ .workspace-snap--left {
73
+ left: 0;
74
+ }
75
+ .workspace-snap--right {
76
+ right: 0;
77
+ }
78
+
79
+ /* Inset horizontally by the same band, so the CORNERS belong unambiguously to
80
+ * left/right — the vertical split is what this feature is for, and an overlap
81
+ * would resolve a corner by centre distance, i.e. arbitrarily. */
82
+ .workspace-snap--top,
83
+ .workspace-snap--bottom {
84
+ left: min(64px, 18%);
85
+ right: min(64px, 18%);
86
+ height: min(64px, 18%);
87
+ }
88
+ .workspace-snap--top {
89
+ top: 0;
90
+ }
91
+ .workspace-snap--bottom {
92
+ bottom: 0;
93
+ }
94
+
95
+ /* Empty state */
96
+ .workspace-empty {
97
+ flex: 1;
98
+ display: flex;
99
+ flex-direction: column;
100
+ align-items: center;
101
+ justify-content: center;
102
+ text-align: center;
103
+ padding: 24px;
104
+ color: var(--text-dim);
105
+ }
106
+
107
+ .workspace-empty-art {
108
+ font-family: var(--font-mono);
109
+ font-size: 13px;
110
+ line-height: 1.25;
111
+ color: color-mix(in oklch, var(--accent) 55%, var(--text-faint));
112
+ margin: 0 0 18px;
113
+ }
114
+
115
+ .workspace-empty-title {
116
+ font-size: 15px;
117
+ font-weight: 600;
118
+ color: var(--text-1);
119
+ margin-bottom: 7px;
120
+ }
121
+
122
+ .workspace-empty-text {
123
+ font-size: 12.5px;
124
+ max-width: 340px;
125
+ line-height: 1.6;
126
+ margin: 0 0 4px;
127
+ }
128
+
129
+ .workspace-empty-text kbd,
130
+ .workspace-empty-text span {
131
+ color: var(--text-1);
132
+ font-family: var(--font-mono);
133
+ }
134
+
135
+ .workspace-empty-hint {
136
+ font-size: 11.5px;
137
+ color: var(--text-faint);
138
+ font-family: var(--font-mono);
139
+ }