2.1.0 (#317)
Co-authored-by: Quentin Torroba <quentin.torroba@mistral.ai> Co-authored-by: Michel Thomazo <michel.thomazo@mistral.ai> Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Vincent Guilloux <vincent.guilloux@mistral.ai> Co-authored-by: Clément Siriex <clement.sirieix@mistral.ai> Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai> Co-authored-by: Nicolas Karolak <nicolas@karolak.fr>
|
|
@ -1,7 +1,5 @@
|
|||
name: Feature Request
|
||||
description: Pitch an improvement for Mistral Vibe
|
||||
title: "feat: "
|
||||
labels: ["enhancement"]
|
||||
title: "[Feature Request]: "
|
||||
labels: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
|
@ -37,7 +35,7 @@ body:
|
|||
attributes:
|
||||
label: Proposed solution
|
||||
description: >
|
||||
Sketch the feature you’d like to see. Code snippets welcome.
|
||||
Sketch the feature you'd like to see. Code snippets welcome.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,5 +1,8 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature request
|
||||
url: https://github.com/mistralai/mistral-vibe/discussions/new?category=ideas
|
||||
about: Suggest a new feature or improvement via GitHub Discussions
|
||||
- name: Ask a question
|
||||
url: https://discord.com/channels/1144547040454508606/1447989080720670915
|
||||
about: Join Mistral AI Discord server for support and discussions
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ extend-ignore-re = ["(?m)^.*(#|//)\\s*typos:disable-line$", "datas"]
|
|||
[default.extend-words]
|
||||
iterm = "iterm"
|
||||
ITERM = "ITERM"
|
||||
asend = "asend"
|
||||
|
|
|
|||
2
.vscode/launch.json
vendored
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "2.0.2",
|
||||
"version": "2.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ACP Server",
|
||||
|
|
|
|||
30
CHANGELOG.md
|
|
@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.1.0] - 2026-02-11
|
||||
|
||||
### Added
|
||||
|
||||
- Incremental load of long sessions: windowing (20 messages), "Load more" to fetch older messages, scroll to bottom when resuming
|
||||
- ACP support for thinking (agent-client-protocol 0.8.0)
|
||||
- Support for FIFO path for env file
|
||||
|
||||
### Changed
|
||||
|
||||
- **UI redesign**: new look and layout for the CLI
|
||||
- Textual UI optimizations: ChatScroll to reduce style recalculations, VerticalGroup for messages, stream layout for streaming blocks, cached DOM queries
|
||||
- Bumped agent-client-protocol to 0.8.0
|
||||
- Use UTC date for timestamps
|
||||
- Clipboard behavior improvements
|
||||
- Docs updated for GitHub discussions
|
||||
- Made the Upgrade to Pro banner less prominent
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed inaccurate token count in UI in some cases
|
||||
- Fixed agent prompt overrides being ignored
|
||||
- Terminal setup: avoid overwriting Wezterm config
|
||||
|
||||
### Removed
|
||||
|
||||
- Legacy terminal theme module and agent indicator widget
|
||||
- Standalone onboarding theme selection screen (replaced by redesign)
|
||||
|
||||
|
||||
## [2.0.2] - 2026-01-30
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -31,11 +31,12 @@ If you encounter a bug, please open an issue with the following information:
|
|||
|
||||
### Feature Requests and Feedback
|
||||
|
||||
We'd love to hear your ideas! When submitting feedback or feature requests:
|
||||
We'd love to hear your ideas! When submitting feedback or feature request discussions:
|
||||
|
||||
1. **Clear Description**: Explain what you'd like to see or improve
|
||||
2. **Use Case**: Describe your use case and why this would be valuable
|
||||
3. **Alternatives**: If applicable, mention any alternatives you've considered
|
||||
1. **Avoid duplicates**: Check opened discussions before creating a new one
|
||||
2. **Clear Description**: Explain what you'd like to see or improve
|
||||
3. **Use Case**: Describe your use case and why this would be valuable
|
||||
4. **Alternatives**: If applicable, mention any alternatives you've considered
|
||||
|
||||
## Development Setup
|
||||
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ Mistral Vibe can be used in text editors and IDEs that support [Agent Client Pro
|
|||
## Resources
|
||||
|
||||
- [CHANGELOG](CHANGELOG.md) - See what's new in each version
|
||||
- [CONTRIBUTING](CONTRIBUTING.md) - Guidelines for feedback and bug reports
|
||||
- [CONTRIBUTING](CONTRIBUTING.md) - Guidelines for feature requests, feedback and bug reports
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
id = "mistral-vibe"
|
||||
name = "Mistral Vibe"
|
||||
description = "Mistral's open-source coding assistant"
|
||||
version = "2.0.2"
|
||||
version = "2.1.0"
|
||||
schema_version = 1
|
||||
authors = ["Mistral AI"]
|
||||
repository = "https://github.com/mistralai/mistral-vibe"
|
||||
|
|
@ -11,25 +11,25 @@ name = "Mistral Vibe"
|
|||
icon = "./icons/mistral_vibe.svg"
|
||||
|
||||
[agent_servers.mistral-vibe.targets.darwin-aarch64]
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.0.2/vibe-acp-darwin-aarch64-2.0.2.zip"
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.1.0/vibe-acp-darwin-aarch64-2.1.0.zip"
|
||||
cmd = "./vibe-acp"
|
||||
|
||||
[agent_servers.mistral-vibe.targets.darwin-x86_64]
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.0.2/vibe-acp-darwin-x86_64-2.0.2.zip"
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.1.0/vibe-acp-darwin-x86_64-2.1.0.zip"
|
||||
cmd = "./vibe-acp"
|
||||
|
||||
[agent_servers.mistral-vibe.targets.linux-aarch64]
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.0.2/vibe-acp-linux-aarch64-2.0.2.zip"
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.1.0/vibe-acp-linux-aarch64-2.1.0.zip"
|
||||
cmd = "./vibe-acp"
|
||||
|
||||
[agent_servers.mistral-vibe.targets.linux-x86_64]
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.0.2/vibe-acp-linux-x86_64-2.0.2.zip"
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.1.0/vibe-acp-linux-x86_64-2.1.0.zip"
|
||||
cmd = "./vibe-acp"
|
||||
|
||||
[agent_servers.mistral-vibe.targets.windows-aarch64]
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.0.2/vibe-acp-windows-aarch64-2.0.2.zip"
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.1.0/vibe-acp-windows-aarch64-2.1.0.zip"
|
||||
cmd = "./vibe-acp.exe"
|
||||
|
||||
[agent_servers.mistral-vibe.targets.windows-x86_64]
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.0.2/vibe-acp-windows-x86_64-2.0.2.zip"
|
||||
archive = "https://github.com/mistralai/mistral-vibe/releases/download/v2.1.0/vibe-acp-windows-x86_64-2.1.0.zip"
|
||||
cmd = "./vibe-acp.exe"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Mistral Vibe Documentation
|
||||
|
||||
Welcome to the Mistral Vibe documentation! For basic setup, see the [main README](https://github.com/mistral-vibe/mistral-vibe#readme).
|
||||
Welcome to the Mistral Vibe documentation! For basic setup, see the [main README](https://github.com/mistralai/mistral-vibe#readme).
|
||||
|
||||
## Available Documentation
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "mistral-vibe"
|
||||
version = "2.0.2"
|
||||
version = "2.1.0"
|
||||
description = "Minimal CLI coding agent by Mistral"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
|
@ -27,7 +27,7 @@ classifiers = [
|
|||
"Topic :: Utilities",
|
||||
]
|
||||
dependencies = [
|
||||
"agent-client-protocol==0.7.1",
|
||||
"agent-client-protocol==0.8.0",
|
||||
"anyio>=4.12.0",
|
||||
"httpx>=0.28.1",
|
||||
"mcp>=1.14.0",
|
||||
|
|
@ -46,6 +46,11 @@ dependencies = [
|
|||
"textual-speedups>=0.2.1",
|
||||
"tree-sitter>=0.25.2",
|
||||
"tree-sitter-bash>=0.25.1",
|
||||
"keyring>=25.6.0",
|
||||
"cryptography>=44.0.0",
|
||||
"zstandard>=0.25.0",
|
||||
"gitpython>=3.1.46",
|
||||
"giturlparse>=0.14.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
|
@ -134,6 +139,8 @@ ignore = ["D203", "D205", "D213", "ANN401", "PLR6301"]
|
|||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/*" = ["ANN", "PLR"]
|
||||
# Textual parent classes don't use ClassVar, so we can't either without breaking pyright
|
||||
"vibe/cli/textual_ui/ansi_markdown.py" = ["RUF012"]
|
||||
|
||||
[tool.ruff.lint.flake8-tidy-imports]
|
||||
ban-relative-imports = "all"
|
||||
|
|
|
|||
|
|
@ -78,6 +78,15 @@ function install_uv() {
|
|||
fi
|
||||
}
|
||||
|
||||
function check_vibe_installed() {
|
||||
if command -v vibe &> /dev/null; then
|
||||
info "vibe is already installed"
|
||||
VIBE_INSTALLED=true
|
||||
else
|
||||
VIBE_INSTALLED=false
|
||||
fi
|
||||
}
|
||||
|
||||
function install_vibe() {
|
||||
info "Installing mistral-vibe from GitHub repository using uv..."
|
||||
uv tool install mistral-vibe
|
||||
|
|
@ -85,6 +94,13 @@ function install_vibe() {
|
|||
success "Mistral Vibe installed successfully! (commands: vibe, vibe-acp)"
|
||||
}
|
||||
|
||||
function update_vibe() {
|
||||
info "Updating mistral-vibe from GitHub repository using uv..."
|
||||
uv tool upgrade mistral-vibe
|
||||
|
||||
success "Mistral Vibe updated successfully!"
|
||||
}
|
||||
|
||||
function main() {
|
||||
echo
|
||||
echo "██████████████████░░"
|
||||
|
|
@ -108,7 +124,13 @@ function main() {
|
|||
install_uv
|
||||
fi
|
||||
|
||||
check_vibe_installed
|
||||
|
||||
if [[ "$VIBE_INSTALLED" == "false" ]]; then
|
||||
install_vibe
|
||||
else
|
||||
update_vibe
|
||||
fi
|
||||
|
||||
if command -v vibe &> /dev/null; then
|
||||
success "Installation completed successfully!"
|
||||
|
|
|
|||
154
tests/acp/test_agent_thought.py
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from acp.schema import AgentThoughtChunk, TextContentBlock
|
||||
import pytest
|
||||
|
||||
from tests.acp.conftest import _create_acp_agent
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from tests.stubs.fake_backend import FakeBackend
|
||||
from tests.stubs.fake_client import FakeClient
|
||||
from vibe.acp.acp_agent_loop import VibeAcpAgentLoop
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.types import LLMChunk, LLMMessage, LLMUsage, Role
|
||||
|
||||
|
||||
def _create_backend_with_reasoning(
|
||||
reasoning_content: str, content: str = "Hi"
|
||||
) -> FakeBackend:
|
||||
return FakeBackend(
|
||||
LLMChunk(
|
||||
message=LLMMessage(
|
||||
role=Role.assistant,
|
||||
content=content,
|
||||
reasoning_content=reasoning_content,
|
||||
),
|
||||
usage=LLMUsage(prompt_tokens=1, completion_tokens=1),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def backend_with_reasoning() -> FakeBackend:
|
||||
return _create_backend_with_reasoning("Let me think about this...")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def acp_agent_loop_with_reasoning(
|
||||
backend_with_reasoning: FakeBackend,
|
||||
) -> VibeAcpAgentLoop:
|
||||
config = build_test_vibe_config(active_model="devstral-latest")
|
||||
|
||||
class PatchedAgentLoop(AgentLoop):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **{**kwargs, "backend": backend_with_reasoning})
|
||||
self._base_config = config
|
||||
self.agent_manager.invalidate_config()
|
||||
|
||||
patch("vibe.acp.acp_agent_loop.AgentLoop", side_effect=PatchedAgentLoop).start()
|
||||
return _create_acp_agent()
|
||||
|
||||
|
||||
class TestACPAgentThought:
|
||||
@pytest.mark.asyncio
|
||||
async def test_prompt_with_reasoning_emits_agent_thought_chunk(
|
||||
self, acp_agent_loop_with_reasoning: VibeAcpAgentLoop
|
||||
) -> None:
|
||||
session_response = await acp_agent_loop_with_reasoning.new_session(
|
||||
cwd=str(Path.cwd()), mcp_servers=[]
|
||||
)
|
||||
session_id = session_response.session_id
|
||||
|
||||
await acp_agent_loop_with_reasoning.prompt(
|
||||
session_id=session_id,
|
||||
prompt=[TextContentBlock(type="text", text="Just say hi")],
|
||||
)
|
||||
|
||||
fake_client: FakeClient = acp_agent_loop_with_reasoning.client # type: ignore
|
||||
thought_updates = [
|
||||
update
|
||||
for update in fake_client._session_updates
|
||||
if isinstance(update.update, AgentThoughtChunk)
|
||||
]
|
||||
|
||||
assert len(thought_updates) == 1
|
||||
thought_chunk = thought_updates[0].update
|
||||
assert thought_chunk.session_update == "agent_thought_chunk"
|
||||
assert thought_chunk.content is not None
|
||||
assert isinstance(thought_chunk.content, TextContentBlock)
|
||||
assert thought_chunk.content.text == "Let me think about this..."
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_prompt_without_reasoning_does_not_emit_agent_thought_chunk(
|
||||
self, acp_agent_loop: VibeAcpAgentLoop
|
||||
) -> None:
|
||||
session_response = await acp_agent_loop.new_session(
|
||||
cwd=str(Path.cwd()), mcp_servers=[]
|
||||
)
|
||||
session_id = session_response.session_id
|
||||
|
||||
await acp_agent_loop.prompt(
|
||||
session_id=session_id,
|
||||
prompt=[TextContentBlock(type="text", text="Just say hi")],
|
||||
)
|
||||
|
||||
fake_client: FakeClient = acp_agent_loop.client # type: ignore
|
||||
thought_updates = [
|
||||
update
|
||||
for update in fake_client._session_updates
|
||||
if isinstance(update.update, AgentThoughtChunk)
|
||||
]
|
||||
|
||||
assert len(thought_updates) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_agent_thought_chunk_contains_text_content_block(
|
||||
self, acp_agent_loop_with_reasoning: VibeAcpAgentLoop
|
||||
) -> None:
|
||||
session_response = await acp_agent_loop_with_reasoning.new_session(
|
||||
cwd=str(Path.cwd()), mcp_servers=[]
|
||||
)
|
||||
session_id = session_response.session_id
|
||||
|
||||
await acp_agent_loop_with_reasoning.prompt(
|
||||
session_id=session_id, prompt=[TextContentBlock(type="text", text="Hello")]
|
||||
)
|
||||
|
||||
fake_client: FakeClient = acp_agent_loop_with_reasoning.client # type: ignore
|
||||
thought_updates = [
|
||||
update
|
||||
for update in fake_client._session_updates
|
||||
if isinstance(update.update, AgentThoughtChunk)
|
||||
]
|
||||
|
||||
assert len(thought_updates) == 1
|
||||
thought_chunk = thought_updates[0].update
|
||||
assert thought_chunk.content.type == "text"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_agent_thought_chunk_contains_message_id(
|
||||
self, acp_agent_loop_with_reasoning: VibeAcpAgentLoop
|
||||
) -> None:
|
||||
session_response = await acp_agent_loop_with_reasoning.new_session(
|
||||
cwd=str(Path.cwd()), mcp_servers=[]
|
||||
)
|
||||
session_id = session_response.session_id
|
||||
|
||||
await acp_agent_loop_with_reasoning.prompt(
|
||||
session_id=session_id, prompt=[TextContentBlock(type="text", text="Hello")]
|
||||
)
|
||||
|
||||
fake_client: FakeClient = acp_agent_loop_with_reasoning.client # type: ignore
|
||||
thought_updates = [
|
||||
update
|
||||
for update in fake_client._session_updates
|
||||
if isinstance(update.update, AgentThoughtChunk)
|
||||
]
|
||||
|
||||
assert len(thought_updates) == 1
|
||||
thought_chunk = thought_updates[0].update
|
||||
assert thought_chunk.field_meta is not None
|
||||
assert "messageId" in thought_chunk.field_meta
|
||||
assert thought_chunk.field_meta["messageId"] is not None
|
||||
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import asyncio
|
||||
|
||||
from acp import CreateTerminalResponse
|
||||
from acp.schema import EnvVariable, TerminalOutputResponse, WaitForTerminalExitResponse
|
||||
import pytest
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ class MockClient:
|
|||
env: list | None = None,
|
||||
output_byte_limit: int | None = None,
|
||||
**kwargs,
|
||||
) -> MockTerminalHandle:
|
||||
) -> CreateTerminalResponse:
|
||||
self._create_terminal_called = True
|
||||
self._last_create_params = {
|
||||
"command": command,
|
||||
|
|
@ -70,7 +71,7 @@ class MockClient:
|
|||
}
|
||||
if self._create_terminal_error:
|
||||
raise self._create_terminal_error
|
||||
return self._terminal_handle
|
||||
return CreateTerminalResponse(terminal_id=self._terminal_handle.id)
|
||||
|
||||
async def terminal_output(
|
||||
self, session_id: str, terminal_id: str, **kwargs
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ from unittest.mock import patch
|
|||
from acp.schema import TextContentBlock, ToolCallProgress, ToolCallStart
|
||||
import pytest
|
||||
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from tests.stubs.fake_backend import FakeBackend
|
||||
from tests.stubs.fake_client import FakeClient
|
||||
from vibe.acp.acp_agent_loop import VibeAcpAgentLoop
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -19,10 +19,7 @@ def acp_agent_loop(backend: FakeBackend) -> VibeAcpAgentLoop:
|
|||
class PatchedAgent(AgentLoop):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
# Force our config with auto_compact_threshold=1
|
||||
kwargs["config"] = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
auto_compact_threshold=1,
|
||||
)
|
||||
kwargs["config"] = build_test_vibe_config(auto_compact_threshold=1)
|
||||
super().__init__(*args, **kwargs, backend=backend)
|
||||
|
||||
patch("vibe.acp.acp_agent_loop.AgentLoop", side_effect=PatchedAgent).start()
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class TestACPInitialize:
|
|||
),
|
||||
)
|
||||
assert response.agent_info == Implementation(
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.0.2"
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.1.0"
|
||||
)
|
||||
|
||||
assert response.auth_methods == []
|
||||
|
|
@ -48,7 +48,7 @@ class TestACPInitialize:
|
|||
),
|
||||
)
|
||||
assert response.agent_info == Implementation(
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.0.2"
|
||||
name="@mistralai/mistral-vibe", title="Mistral Vibe", version="2.1.0"
|
||||
)
|
||||
|
||||
assert response.auth_methods is not None
|
||||
|
|
|
|||
|
|
@ -6,15 +6,16 @@ from unittest.mock import patch
|
|||
import pytest
|
||||
|
||||
from tests.acp.conftest import _create_acp_agent
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from vibe.acp.acp_agent_loop import VibeAcpAgentLoop
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.agents.models import BuiltinAgentName
|
||||
from vibe.core.config import ModelConfig, VibeConfig
|
||||
from vibe.core.config import ModelConfig
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def acp_agent_loop(backend) -> VibeAcpAgentLoop:
|
||||
config = VibeConfig(
|
||||
config = build_test_vibe_config(
|
||||
active_model="devstral-latest",
|
||||
models=[
|
||||
ModelConfig(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from unittest.mock import patch
|
|||
import pytest
|
||||
|
||||
from tests.acp.conftest import _create_acp_agent
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from vibe.acp.acp_agent_loop import VibeAcpAgentLoop
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.config import ModelConfig, VibeConfig
|
||||
|
|
@ -14,7 +15,7 @@ from vibe.core.types import LLMMessage, Role
|
|||
|
||||
@pytest.fixture
|
||||
def acp_agent_loop(backend) -> VibeAcpAgentLoop:
|
||||
config = VibeConfig(
|
||||
config = build_test_vibe_config(
|
||||
active_model="devstral-latest",
|
||||
models=[
|
||||
ModelConfig(
|
||||
|
|
|
|||
|
|
@ -10,19 +10,6 @@ from textual.widgets import Markdown
|
|||
from vibe.cli.textual_ui.app import VibeApp
|
||||
from vibe.cli.textual_ui.widgets.chat_input.completion_popup import CompletionPopup
|
||||
from vibe.cli.textual_ui.widgets.chat_input.container import ChatInputContainer
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vibe_config() -> VibeConfig:
|
||||
return VibeConfig(session_logging=SessionLoggingConfig(enabled=False))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vibe_app(vibe_config: VibeConfig) -> VibeApp:
|
||||
agent_loop = AgentLoop(vibe_config)
|
||||
return VibeApp(agent_loop=agent_loop)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
import logging
|
||||
from os import environ
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -9,8 +11,23 @@ from vibe.cli.plan_offer.decide_plan_offer import (
|
|||
PlanOfferAction,
|
||||
PlanType,
|
||||
decide_plan_offer,
|
||||
resolve_api_key_for_plan,
|
||||
)
|
||||
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIResponse
|
||||
from vibe.core.config import Backend, ProviderConfig
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mistral_api_key_env() -> Generator[str, None, None]:
|
||||
original_value = environ.get("MISTRAL_API_KEY")
|
||||
test_api_key = "test_mistral_api_key"
|
||||
environ["MISTRAL_API_KEY"] = test_api_key
|
||||
yield test_api_key
|
||||
|
||||
if original_value is not None:
|
||||
environ["MISTRAL_API_KEY"] = original_value
|
||||
else:
|
||||
del environ["MISTRAL_API_KEY"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -115,3 +132,51 @@ async def test_proposes_none_and_logs_warning_when_gateway_error_occurs(
|
|||
assert plan_type is PlanType.UNKNOWN
|
||||
assert gateway.calls == ["api-key"]
|
||||
assert "Failed to fetch plan status." in caplog.text
|
||||
|
||||
|
||||
def test_resolve_api_key_for_plan_with_mistral_backend(
|
||||
mistral_api_key_env: str,
|
||||
) -> None:
|
||||
test_api_key = mistral_api_key_env
|
||||
|
||||
provider = ProviderConfig(
|
||||
name="test_mistral",
|
||||
api_base="https://api.mistral.ai",
|
||||
backend=Backend.MISTRAL,
|
||||
api_key_env_var="MISTRAL_API_KEY",
|
||||
)
|
||||
|
||||
result = resolve_api_key_for_plan(provider)
|
||||
assert result == test_api_key
|
||||
|
||||
|
||||
def test_resolve_api_key_for_plan_with_non_mistral_backend(
|
||||
mistral_api_key_env: str,
|
||||
) -> None:
|
||||
provider = ProviderConfig(
|
||||
name="test_generic",
|
||||
api_base="https://api.generic.ai",
|
||||
backend=Backend.GENERIC,
|
||||
api_key_env_var="GENERIC_API_KEY",
|
||||
)
|
||||
|
||||
result = resolve_api_key_for_plan(provider)
|
||||
assert result == mistral_api_key_env
|
||||
|
||||
|
||||
def test_resolve_api_key_for_plan_with_missing_env_var() -> None:
|
||||
previous_api_key = environ["MISTRAL_API_KEY"]
|
||||
del environ["MISTRAL_API_KEY"]
|
||||
|
||||
provider = ProviderConfig(
|
||||
name="test_mistral",
|
||||
api_base="https://api.mistral.ai",
|
||||
backend=Backend.MISTRAL,
|
||||
api_key_env_var="MISTRAL_API_KEY",
|
||||
)
|
||||
|
||||
result = resolve_api_key_for_plan(provider)
|
||||
assert result is None
|
||||
|
||||
if previous_api_key is not None:
|
||||
environ["MISTRAL_API_KEY"] = previous_api_key
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.cli.plan_offer.adapters.fake_whoami_gateway import FakeWhoAmIGateway
|
||||
from tests.stubs.fake_backend import FakeBackend
|
||||
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIResponse
|
||||
from vibe.cli.textual_ui.app import VibeApp
|
||||
from vibe.cli.textual_ui.widgets.messages import PlanOfferMessage
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
|
||||
|
||||
def _make_app(gateway: FakeWhoAmIGateway, config: VibeConfig | None = None) -> VibeApp:
|
||||
config = config or VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False), enable_update_checks=False
|
||||
)
|
||||
agent_loop = AgentLoop(config=config, backend=FakeBackend())
|
||||
return VibeApp(agent_loop=agent_loop, plan_offer_gateway=gateway)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_app_shows_upgrade_offer_in_plan_offer_message(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setenv("MISTRAL_API_KEY", "api-key")
|
||||
gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=False,
|
||||
advertise_pro_plan=True,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
app = _make_app(gateway)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.1)
|
||||
offer = app.query_one(PlanOfferMessage)
|
||||
assert "Upgrade to" in offer.get_text()
|
||||
assert "Pro" in offer.get_text()
|
||||
assert gateway.calls == ["api-key"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_app_shows_switch_to_pro_key_offer_in_plan_offer_message(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setenv("MISTRAL_API_KEY", "api-key")
|
||||
gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=False,
|
||||
advertise_pro_plan=False,
|
||||
prompt_switching_to_pro_plan=True,
|
||||
)
|
||||
)
|
||||
app = _make_app(gateway)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.1)
|
||||
offer = app.query_one(PlanOfferMessage)
|
||||
assert "Switch to your" in offer.get_text()
|
||||
assert "Pro API key" in offer.get_text()
|
||||
assert gateway.calls == ["api-key"]
|
||||
114
tests/cli/test_braille_renderer.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from vibe.cli.textual_ui.widgets.braille_renderer import (
|
||||
_braille_char_from_dot_indices,
|
||||
_braille_dot_index,
|
||||
render_braille,
|
||||
)
|
||||
|
||||
|
||||
class TestBrailleDotIndex:
|
||||
"""Tests for _braille_dot_index(x, y)."""
|
||||
|
||||
def test_dot_positions_per_docstring(self) -> None:
|
||||
# Layout from docstring: x in {0,1}, y in {0,1,2,3}
|
||||
# -x-> | 1 4 | 2 5 | 3 6 | V 7 8
|
||||
assert _braille_dot_index(0, 0) == 1
|
||||
assert _braille_dot_index(1, 0) == 4
|
||||
assert _braille_dot_index(0, 1) == 2
|
||||
assert _braille_dot_index(1, 1) == 5
|
||||
assert _braille_dot_index(0, 2) == 3
|
||||
assert _braille_dot_index(1, 2) == 6
|
||||
assert _braille_dot_index(0, 3) == 7
|
||||
assert _braille_dot_index(1, 3) == 8
|
||||
|
||||
@pytest.mark.parametrize("x", [0, 1])
|
||||
@pytest.mark.parametrize("y", [0, 1, 2, 3])
|
||||
def test_all_indices_in_range_one_to_eight(self, x: int, y: int) -> None:
|
||||
idx = _braille_dot_index(x, y)
|
||||
assert 1 <= idx <= 8
|
||||
|
||||
|
||||
class TestBrailleCharFromDotIndices:
|
||||
"""Tests for _braille_char_from_dot_indices."""
|
||||
|
||||
def test_empty_indices_returns_space(self) -> None:
|
||||
assert _braille_char_from_dot_indices([]) == " "
|
||||
|
||||
def test_single_dot_one_returns_braille_char(self) -> None:
|
||||
# U+2800 is empty, +1 for dot 1 = U+2801
|
||||
char = _braille_char_from_dot_indices([1])
|
||||
assert char == "⠁"
|
||||
|
||||
def test_all_dots_returns_full_cell(self) -> None:
|
||||
char = _braille_char_from_dot_indices([1, 2, 3, 4, 5, 6, 7, 8])
|
||||
# Full block: 0x2800 + (2^8 - 1) = 0x28FF
|
||||
assert char == "⣿"
|
||||
|
||||
def test_invalid_index_below_one_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="Invalid braille dot indices"):
|
||||
_braille_char_from_dot_indices([0])
|
||||
|
||||
def test_invalid_index_above_eight_raises(self) -> None:
|
||||
with pytest.raises(ValueError, match="Invalid braille dot indices"):
|
||||
_braille_char_from_dot_indices([9])
|
||||
|
||||
def test_order_of_indices_does_not_matter(self) -> None:
|
||||
a = _braille_char_from_dot_indices([1, 2, 3])
|
||||
b = _braille_char_from_dot_indices([3, 1, 2])
|
||||
assert a == b
|
||||
|
||||
|
||||
class TestRenderBraille:
|
||||
"""Tests for render_braille(dot_coords, width, height)."""
|
||||
|
||||
def test_empty_coords_produces_blank_grid(self) -> None:
|
||||
result = render_braille([], width=4, height=2)
|
||||
assert result == " "
|
||||
|
||||
def test_origin_one_dot(self) -> None:
|
||||
# (0, 0) -> first cell, dot 1
|
||||
result = render_braille([0], width=2, height=4)
|
||||
lines = result.split("\n")
|
||||
assert len(lines) == 1
|
||||
assert lines[0][0] == "⠁"
|
||||
assert lines[0].strip() == "⠁"
|
||||
|
||||
def test_output_dimensions_match_ceiled_width_and_height(self) -> None:
|
||||
result = render_braille([], width=3, height=7)
|
||||
lines = result.split("\n")
|
||||
assert len(lines) == 2
|
||||
assert len(lines[0]) == 2
|
||||
|
||||
def test_multiple_dots_in_same_cell_combine(self) -> None:
|
||||
# Two dots in first cell: (0,0) and (1,0) -> indices 1 and 4
|
||||
result = render_braille([0, 1], width=4, height=4)
|
||||
lines = result.split("\n")
|
||||
assert len(lines) == 1
|
||||
assert lines[0][0] == "⠉"
|
||||
|
||||
def test_dots_in_different_cells(self) -> None:
|
||||
# First cell (0,0), second cell (2,0)
|
||||
result = render_braille([0, 2], width=4, height=4)
|
||||
lines = result.split("\n")
|
||||
assert len(lines) == 1
|
||||
assert len(lines[0]) == 2
|
||||
assert lines[0][0] == "⠁"
|
||||
assert lines[0][1] == "⠁"
|
||||
|
||||
def test_multiple_rows(self) -> None:
|
||||
# Dot at (0,0) and (0,4) -> two rows
|
||||
result = render_braille([0, 4j], width=2, height=8)
|
||||
lines = result.split("\n")
|
||||
assert len(lines) == 2
|
||||
assert lines[0][0] == "⠁"
|
||||
assert lines[1][0] == "⠁"
|
||||
|
||||
def test_accepts_complex_coords(self) -> None:
|
||||
result = render_braille([1 + 2j], width=4, height=4)
|
||||
lines = result.split("\n")
|
||||
assert len(lines) == 1
|
||||
# x=1,y=2 -> first cell (0,0), sub_x=1, sub_y=2 -> dot index 6
|
||||
assert lines[0][0] == _braille_char_from_dot_indices([6])
|
||||
|
|
@ -5,17 +5,10 @@ from types import SimpleNamespace
|
|||
from typing import cast
|
||||
from unittest.mock import MagicMock, mock_open, patch
|
||||
|
||||
import pyperclip
|
||||
import pytest
|
||||
from textual.app import App
|
||||
|
||||
from vibe.cli.clipboard import (
|
||||
_copy_osc52,
|
||||
_copy_wayland_clipboard,
|
||||
_copy_x11_clipboard,
|
||||
_get_copy_fns,
|
||||
copy_selection_to_clipboard,
|
||||
)
|
||||
from vibe.cli.clipboard import _copy_osc52, copy_selection_to_clipboard
|
||||
|
||||
|
||||
class MockWidget:
|
||||
|
|
@ -42,7 +35,6 @@ def mock_app() -> App:
|
|||
app = MagicMock(spec=App)
|
||||
app.query = MagicMock(return_value=[])
|
||||
app.notify = MagicMock()
|
||||
app.copy_to_clipboard = MagicMock()
|
||||
return cast(App, app)
|
||||
|
||||
|
||||
|
|
@ -86,21 +78,18 @@ def test_copy_selection_to_clipboard_no_notification(
|
|||
mock_app.notify.assert_not_called()
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard._get_copy_fns")
|
||||
@patch("vibe.cli.clipboard._copy_osc52")
|
||||
def test_copy_selection_to_clipboard_success(
|
||||
mock_get_copy_fns: MagicMock, mock_app: MagicMock
|
||||
mock_copy_osc52: MagicMock, mock_app: MagicMock
|
||||
) -> None:
|
||||
widget = MockWidget(
|
||||
text_selection=SimpleNamespace(), get_selection_result=("selected text", None)
|
||||
)
|
||||
mock_app.query.return_value = [widget]
|
||||
|
||||
mock_copy_fn = MagicMock()
|
||||
mock_get_copy_fns.return_value = [mock_copy_fn]
|
||||
|
||||
copy_selection_to_clipboard(mock_app)
|
||||
|
||||
mock_copy_fn.assert_called_once_with("selected text")
|
||||
mock_copy_osc52.assert_called_once_with("selected text")
|
||||
mock_app.notify.assert_called_once_with(
|
||||
'"selected text" copied to clipboard',
|
||||
severity="information",
|
||||
|
|
@ -109,54 +98,22 @@ def test_copy_selection_to_clipboard_success(
|
|||
)
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard._get_copy_fns")
|
||||
def test_copy_selection_to_clipboard_tries_all(
|
||||
mock_get_copy_fns: MagicMock, mock_app: MagicMock
|
||||
@patch("vibe.cli.clipboard._copy_osc52")
|
||||
def test_copy_selection_to_clipboard_failure(
|
||||
mock_copy_osc52: MagicMock, mock_app: MagicMock
|
||||
) -> None:
|
||||
widget = MockWidget(
|
||||
text_selection=SimpleNamespace(), get_selection_result=("selected text", None)
|
||||
)
|
||||
mock_app.query.return_value = [widget]
|
||||
|
||||
fn_1 = MagicMock(side_effect=Exception("failed"))
|
||||
fn_2 = MagicMock()
|
||||
fn_3 = MagicMock()
|
||||
mock_get_copy_fns.return_value = [fn_1, fn_2, fn_3]
|
||||
mock_copy_osc52.side_effect = Exception("OSC52 failed")
|
||||
|
||||
copy_selection_to_clipboard(mock_app)
|
||||
|
||||
fn_1.assert_called_once_with("selected text")
|
||||
fn_2.assert_called_once_with("selected text")
|
||||
fn_3.assert_called_once_with("selected text")
|
||||
mock_copy_osc52.assert_called_once_with("selected text")
|
||||
mock_app.notify.assert_called_once_with(
|
||||
'"selected text" copied to clipboard',
|
||||
severity="information",
|
||||
timeout=2,
|
||||
markup=False,
|
||||
)
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard._get_copy_fns")
|
||||
def test_copy_selection_to_clipboard_all_methods_fail(
|
||||
mock_get_copy_fns: MagicMock, mock_app: MagicMock
|
||||
) -> None:
|
||||
widget = MockWidget(
|
||||
text_selection=SimpleNamespace(), get_selection_result=("selected text", None)
|
||||
)
|
||||
mock_app.query.return_value = [widget]
|
||||
|
||||
failing_fn1 = MagicMock(side_effect=Exception("failed 1"))
|
||||
failing_fn2 = MagicMock(side_effect=Exception("failed 2"))
|
||||
failing_fn3 = MagicMock(side_effect=Exception("failed 3"))
|
||||
mock_get_copy_fns.return_value = [failing_fn1, failing_fn2, failing_fn3]
|
||||
|
||||
copy_selection_to_clipboard(mock_app)
|
||||
|
||||
failing_fn1.assert_called_once_with("selected text")
|
||||
failing_fn2.assert_called_once_with("selected text")
|
||||
failing_fn3.assert_called_once_with("selected text")
|
||||
mock_app.notify.assert_called_once_with(
|
||||
"Failed to copy - no clipboard method available", severity="warning", timeout=3
|
||||
"Failed to copy - clipboard not available", severity="warning", timeout=3
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -171,14 +128,12 @@ def test_copy_selection_to_clipboard_multiple_widgets(mock_app: MagicMock) -> No
|
|||
widget3 = MockWidget(text_selection=None)
|
||||
mock_app.query.return_value = [widget1, widget2, widget3]
|
||||
|
||||
with patch("vibe.cli.clipboard._get_copy_fns") as mock_get_copy_fns:
|
||||
mock_copy_fn = MagicMock()
|
||||
mock_get_copy_fns.return_value = [mock_copy_fn]
|
||||
with patch("vibe.cli.clipboard._copy_osc52") as mock_copy_osc52:
|
||||
copy_selection_to_clipboard(mock_app)
|
||||
|
||||
mock_copy_fn.assert_called_once_with("first selection\nsecond selection")
|
||||
mock_copy_osc52.assert_called_once_with("first selection\nsecond selection")
|
||||
mock_app.notify.assert_called_once_with(
|
||||
'"first selection⏎second selection" copied to clipboard',
|
||||
'"first selection\u23cesecond selection" copied to clipboard',
|
||||
severity="information",
|
||||
timeout=2,
|
||||
markup=False,
|
||||
|
|
@ -192,12 +147,10 @@ def test_copy_selection_to_clipboard_preview_shortening(mock_app: MagicMock) ->
|
|||
)
|
||||
mock_app.query.return_value = [widget]
|
||||
|
||||
with patch("vibe.cli.clipboard._get_copy_fns") as mock_get_copy_fns:
|
||||
mock_copy_fn = MagicMock()
|
||||
mock_get_copy_fns.return_value = [mock_copy_fn]
|
||||
with patch("vibe.cli.clipboard._copy_osc52") as mock_copy_osc52:
|
||||
copy_selection_to_clipboard(mock_app)
|
||||
|
||||
mock_copy_fn.assert_called_once_with(long_text)
|
||||
mock_copy_osc52.assert_called_once_with(long_text)
|
||||
notification_call = mock_app.notify.call_args
|
||||
assert notification_call is not None
|
||||
assert '"' in notification_call[0][0]
|
||||
|
|
@ -210,7 +163,7 @@ def test_copy_osc52_writes_correct_sequence(
|
|||
mock_file: MagicMock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
monkeypatch.delenv("TMUX", raising=False)
|
||||
test_text = "héllo wörld 🎉"
|
||||
test_text = "hello world"
|
||||
|
||||
_copy_osc52(test_text)
|
||||
|
||||
|
|
@ -237,109 +190,16 @@ def test_copy_osc52_with_tmux(
|
|||
handle.write.assert_called_once_with(expected_seq)
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard.subprocess.run")
|
||||
def test_copy_x11_clipboard(mock_subprocess: MagicMock) -> None:
|
||||
test_text = "test text"
|
||||
|
||||
_copy_x11_clipboard(test_text)
|
||||
|
||||
mock_subprocess.assert_called_once_with(
|
||||
["xclip", "-selection", "clipboard"],
|
||||
input=test_text.encode("utf-8"),
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard.subprocess.run")
|
||||
def test_copy_wayland_clipboard(mock_subprocess: MagicMock) -> None:
|
||||
test_text = "test text"
|
||||
|
||||
_copy_wayland_clipboard(test_text)
|
||||
|
||||
mock_subprocess.assert_called_once_with(
|
||||
["wl-copy"], input=test_text.encode("utf-8"), check=True
|
||||
)
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard.shutil.which")
|
||||
def test_get_copy_fns_no_system_tools(mock_which: MagicMock, mock_app: App) -> None:
|
||||
mock_which.return_value = None
|
||||
|
||||
copy_fns = _get_copy_fns(mock_app)
|
||||
|
||||
assert len(copy_fns) == 3
|
||||
assert copy_fns[0] == _copy_osc52
|
||||
assert copy_fns[1] == pyperclip.copy
|
||||
assert copy_fns[2] == mock_app.copy_to_clipboard
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard.platform.system")
|
||||
@patch("vibe.cli.clipboard.shutil.which")
|
||||
def test_get_copy_fns_with_xclip(
|
||||
mock_which: MagicMock, mock_platform_system: MagicMock, mock_app: App
|
||||
@patch("builtins.open", new_callable=mock_open)
|
||||
def test_copy_osc52_unicode(
|
||||
mock_file: MagicMock, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
mock_platform_system.return_value = "Linux"
|
||||
monkeypatch.delenv("TMUX", raising=False)
|
||||
test_text = "hello world"
|
||||
|
||||
def which_side_effect(cmd: str) -> str | None:
|
||||
return "/usr/bin/xclip" if cmd == "xclip" else None
|
||||
_copy_osc52(test_text)
|
||||
|
||||
mock_which.side_effect = which_side_effect
|
||||
|
||||
copy_fns = _get_copy_fns(mock_app)
|
||||
|
||||
assert len(copy_fns) == 4
|
||||
assert copy_fns[0] == _copy_x11_clipboard
|
||||
assert copy_fns[1] == _copy_osc52
|
||||
assert copy_fns[2] == pyperclip.copy
|
||||
assert copy_fns[3] == mock_app.copy_to_clipboard
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard.platform.system")
|
||||
@patch("vibe.cli.clipboard.shutil.which")
|
||||
def test_get_copy_fns_with_wl_copy(
|
||||
mock_which: MagicMock, mock_platform_system: MagicMock, mock_app: App
|
||||
) -> None:
|
||||
mock_platform_system.return_value = "Linux"
|
||||
|
||||
def which_side_effect(cmd: str) -> str | None:
|
||||
return "/usr/bin/wl-copy" if cmd == "wl-copy" else None
|
||||
|
||||
mock_which.side_effect = which_side_effect
|
||||
|
||||
copy_fns = _get_copy_fns(mock_app)
|
||||
|
||||
assert len(copy_fns) == 4
|
||||
assert copy_fns[0] == _copy_wayland_clipboard
|
||||
assert copy_fns[1] == _copy_osc52
|
||||
assert copy_fns[2] == pyperclip.copy
|
||||
assert copy_fns[3] == mock_app.copy_to_clipboard
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard.platform.system")
|
||||
@patch("vibe.cli.clipboard.shutil.which")
|
||||
def test_get_copy_fns_with_both_system_tools(
|
||||
mock_which: MagicMock, mock_platform_system: MagicMock, mock_app: App
|
||||
) -> None:
|
||||
mock_platform_system.return_value = "Linux"
|
||||
|
||||
def which_side_effect(cmd: str) -> str | None:
|
||||
match cmd:
|
||||
case "wl-copy":
|
||||
return "/usr/bin/wl-copy"
|
||||
case "xclip":
|
||||
return "/usr/bin/xclip"
|
||||
case _:
|
||||
return None
|
||||
|
||||
mock_which.side_effect = which_side_effect
|
||||
|
||||
copy_fns = _get_copy_fns(mock_app)
|
||||
|
||||
assert len(copy_fns) == 5
|
||||
# xclip is checked last, so it's added last and ends up first in the list
|
||||
assert copy_fns[0] == _copy_x11_clipboard
|
||||
# wl-copy is checked first, so it's added before xclip
|
||||
assert copy_fns[1] == _copy_wayland_clipboard
|
||||
assert copy_fns[2] == _copy_osc52
|
||||
assert copy_fns[3] == pyperclip.copy
|
||||
assert copy_fns[4] == mock_app.copy_to_clipboard
|
||||
encoded = base64.b64encode(test_text.encode("utf-8")).decode("ascii")
|
||||
expected_seq = f"\033]52;c;{encoded}\a"
|
||||
handle = mock_file()
|
||||
handle.write.assert_called_once_with(expected_seq)
|
||||
|
|
|
|||
59
tests/cli/test_copy_shortcuts.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.snapshots.base_snapshot_test_app import BaseSnapshotTestApp
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ctrl_y_triggers_copy_selection() -> None:
|
||||
"""Test that ctrl+y keybinding triggers copy_selection_to_clipboard."""
|
||||
app = BaseSnapshotTestApp()
|
||||
|
||||
with patch("vibe.cli.textual_ui.app.copy_selection_to_clipboard") as mock_copy:
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.press("ctrl+y")
|
||||
mock_copy.assert_called_once_with(app, show_toast=False)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ctrl_shift_c_triggers_copy_selection() -> None:
|
||||
"""Test that ctrl+shift+c keybinding triggers copy_selection_to_clipboard."""
|
||||
app = BaseSnapshotTestApp()
|
||||
|
||||
with patch("vibe.cli.textual_ui.app.copy_selection_to_clipboard") as mock_copy:
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.press("ctrl+shift+c")
|
||||
mock_copy.assert_called_once_with(app, show_toast=False)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mouse_up_respects_autocopy_config_enabled() -> None:
|
||||
"""Test that mouse up copies when autocopy_to_clipboard is True."""
|
||||
from tests.snapshots.base_snapshot_test_app import default_config
|
||||
|
||||
config = default_config()
|
||||
config.autocopy_to_clipboard = True
|
||||
app = BaseSnapshotTestApp(config=config)
|
||||
|
||||
with patch("vibe.cli.textual_ui.app.copy_selection_to_clipboard") as mock_copy:
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.click()
|
||||
mock_copy.assert_called_once_with(app, show_toast=True)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mouse_up_respects_autocopy_config_disabled() -> None:
|
||||
"""Test that mouse up does not copy when autocopy_to_clipboard is False."""
|
||||
from tests.snapshots.base_snapshot_test_app import default_config
|
||||
|
||||
config = default_config()
|
||||
config.autocopy_to_clipboard = False
|
||||
app = BaseSnapshotTestApp(config=config)
|
||||
|
||||
with patch("vibe.cli.textual_ui.app.copy_selection_to_clipboard") as mock_copy:
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.click()
|
||||
mock_copy.assert_not_called()
|
||||
16
tests/cli/test_spinner.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
|
||||
from vibe.cli.textual_ui.widgets.spinner import SpinnerType, create_spinner
|
||||
|
||||
|
||||
def test_generate_100_frames_no_crash() -> None:
|
||||
"""Generate 100 frames per spinner type with seeded random for determinism."""
|
||||
random.seed(42)
|
||||
for spinner_type in SpinnerType:
|
||||
spinner = create_spinner(spinner_type)
|
||||
for _ in range(100):
|
||||
frame = spinner.next_frame()
|
||||
assert isinstance(frame, str)
|
||||
assert len(frame) > 0
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from textual.selection import Selection
|
||||
|
|
@ -8,8 +8,6 @@ from textual.widget import Widget
|
|||
|
||||
from vibe.cli.clipboard import copy_selection_to_clipboard
|
||||
from vibe.cli.textual_ui.app import VibeApp
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
|
||||
|
||||
class ClipboardSelectionWidget(Widget):
|
||||
|
|
@ -27,24 +25,15 @@ class ClipboardSelectionWidget(Widget):
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_clipboard_notification_does_not_crash_on_markup_text(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
monkeypatch: pytest.MonkeyPatch, vibe_app: VibeApp
|
||||
) -> None:
|
||||
agent_loop = AgentLoop(
|
||||
config=VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
enable_update_checks=False,
|
||||
)
|
||||
)
|
||||
app = VibeApp(agent_loop=agent_loop)
|
||||
|
||||
async with app.run_test(notifications=True) as pilot:
|
||||
await app.mount(ClipboardSelectionWidget("[/]"))
|
||||
with patch("vibe.cli.clipboard._get_copy_fns") as mock_get_copy_fns:
|
||||
mock_get_copy_fns.return_value = [MagicMock()]
|
||||
copy_selection_to_clipboard(app)
|
||||
async with vibe_app.run_test(notifications=True) as pilot:
|
||||
await vibe_app.mount(ClipboardSelectionWidget("[/]"))
|
||||
with patch("vibe.cli.clipboard._copy_osc52"):
|
||||
copy_selection_to_clipboard(vibe_app)
|
||||
|
||||
await pilot.pause(0.1)
|
||||
notifications = list(app._notifications)
|
||||
notifications = list(vibe_app._notifications)
|
||||
assert notifications
|
||||
notification = notifications[-1]
|
||||
assert notification.markup is False
|
||||
|
|
|
|||
172
tests/cli/test_ui_session_incremental_renderer.py
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
|
||||
import pytest
|
||||
from textual.widgets import Button
|
||||
|
||||
from tests.cli.plan_offer.adapters.fake_whoami_gateway import FakeWhoAmIGateway
|
||||
from tests.conftest import build_test_agent_loop
|
||||
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIResponse
|
||||
from vibe.cli.textual_ui.app import ChatScroll, VibeApp
|
||||
from vibe.cli.textual_ui.widgets.load_more import (
|
||||
HistoryLoadMoreMessage,
|
||||
HistoryLoadMoreRequested,
|
||||
)
|
||||
from vibe.cli.textual_ui.widgets.messages import UserMessage
|
||||
from vibe.cli.textual_ui.windowing import (
|
||||
HISTORY_RESUME_TAIL_MESSAGES,
|
||||
LOAD_MORE_BATCH_SIZE,
|
||||
)
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
from vibe.core.types import LLMMessage, Role
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vibe_config() -> VibeConfig:
|
||||
return VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False), enable_update_checks=False
|
||||
)
|
||||
|
||||
|
||||
def _pro_plan_gateway() -> FakeWhoAmIGateway:
|
||||
return FakeWhoAmIGateway(
|
||||
response=WhoAmIResponse(
|
||||
is_pro_plan=True,
|
||||
advertise_pro_plan=False,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def _wait_until(pause, predicate, timeout: float = 2.0) -> None:
|
||||
start = time.monotonic()
|
||||
while (time.monotonic() - start) < timeout:
|
||||
if predicate():
|
||||
return
|
||||
await pause(0.02)
|
||||
raise AssertionError("Condition was not met within the timeout")
|
||||
|
||||
|
||||
async def _wait_for_load_more(app: VibeApp, pause) -> None:
|
||||
await _wait_until(
|
||||
pause, lambda: len(app.query(HistoryLoadMoreMessage)) == 1, timeout=5.0
|
||||
)
|
||||
|
||||
|
||||
def _load_more_remaining(app: VibeApp) -> int:
|
||||
label = app.query_one(HistoryLoadMoreMessage).query_one(Button).label
|
||||
text = str(label)
|
||||
_, _, remainder = text.rpartition("(")
|
||||
return int(remainder.rstrip(")"))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_session_incremental_loader_shows_tail_and_load_more(
|
||||
vibe_config: VibeConfig,
|
||||
) -> None:
|
||||
agent_loop = build_test_agent_loop(config=vibe_config, enable_streaming=False)
|
||||
agent_loop.messages.extend([
|
||||
LLMMessage(role=Role.user, content=f"msg-{idx}") for idx in range(66)
|
||||
])
|
||||
|
||||
app = VibeApp(agent_loop=agent_loop, plan_offer_gateway=_pro_plan_gateway())
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await _wait_until(
|
||||
pilot.pause,
|
||||
lambda: len(app.query(UserMessage)) == HISTORY_RESUME_TAIL_MESSAGES,
|
||||
timeout=5.0,
|
||||
)
|
||||
await _wait_for_load_more(app, pilot.pause)
|
||||
|
||||
assert len(app.query(UserMessage)) == HISTORY_RESUME_TAIL_MESSAGES
|
||||
load_more = app.query_one(HistoryLoadMoreMessage)
|
||||
label = load_more.query_one(Button).label
|
||||
assert "(" in str(label)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_session_incremental_loader_load_more_shows_remaining_count(
|
||||
vibe_config: VibeConfig,
|
||||
) -> None:
|
||||
total_messages = 31
|
||||
agent_loop = build_test_agent_loop(config=vibe_config, enable_streaming=False)
|
||||
agent_loop.messages.extend([
|
||||
LLMMessage(role=Role.user, content=f"msg-{idx}")
|
||||
for idx in range(total_messages)
|
||||
])
|
||||
|
||||
app = VibeApp(agent_loop=agent_loop, plan_offer_gateway=_pro_plan_gateway())
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await _wait_until(
|
||||
pilot.pause,
|
||||
lambda: len(app.query(UserMessage)) == HISTORY_RESUME_TAIL_MESSAGES,
|
||||
timeout=5.0,
|
||||
)
|
||||
await _wait_for_load_more(app, pilot.pause)
|
||||
|
||||
initial_remaining = total_messages - HISTORY_RESUME_TAIL_MESSAGES
|
||||
assert _load_more_remaining(app) == initial_remaining
|
||||
|
||||
app.post_message(HistoryLoadMoreRequested())
|
||||
expected_remaining = initial_remaining - LOAD_MORE_BATCH_SIZE
|
||||
await _wait_until(
|
||||
pilot.pause,
|
||||
lambda: _load_more_remaining(app) == expected_remaining,
|
||||
timeout=5.0,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_session_incremental_loader_load_more_batches_until_done(
|
||||
vibe_config: VibeConfig,
|
||||
) -> None:
|
||||
agent_loop = build_test_agent_loop(config=vibe_config, enable_streaming=False)
|
||||
agent_loop.messages.extend([
|
||||
LLMMessage(role=Role.user, content=f"msg-{idx}") for idx in range(31)
|
||||
])
|
||||
|
||||
app = VibeApp(agent_loop=agent_loop, plan_offer_gateway=_pro_plan_gateway())
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await _wait_until(
|
||||
pilot.pause,
|
||||
lambda: len(app.query(UserMessage)) == HISTORY_RESUME_TAIL_MESSAGES,
|
||||
timeout=5.0,
|
||||
)
|
||||
await _wait_for_load_more(app, pilot.pause)
|
||||
|
||||
total_messages = 31
|
||||
while len(app.query(HistoryLoadMoreMessage)) == 1:
|
||||
current_count = len(app.query(UserMessage))
|
||||
app.post_message(HistoryLoadMoreRequested())
|
||||
await _wait_until(
|
||||
pilot.pause,
|
||||
lambda current_count=current_count: len(app.query(UserMessage))
|
||||
> current_count,
|
||||
)
|
||||
|
||||
await _wait_until(
|
||||
pilot.pause, lambda: len(app.query(UserMessage)) == total_messages
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_session_incremental_loader_keeps_top_alignment_when_not_scrollable(
|
||||
vibe_config: VibeConfig,
|
||||
) -> None:
|
||||
agent_loop = build_test_agent_loop(config=vibe_config, enable_streaming=False)
|
||||
agent_loop.messages.extend([
|
||||
LLMMessage(role=Role.user, content=f"msg-{idx}")
|
||||
for idx in range(HISTORY_RESUME_TAIL_MESSAGES + 1)
|
||||
])
|
||||
|
||||
app = VibeApp(agent_loop=agent_loop, plan_offer_gateway=_pro_plan_gateway())
|
||||
|
||||
async with app.run_test(size=(120, 80)) as pilot:
|
||||
await _wait_for_load_more(app, pilot.pause)
|
||||
chat = app.query_one("#chat", ChatScroll)
|
||||
assert chat.max_scroll_y == 0
|
||||
assert chat.scroll_y == 0
|
||||
|
|
@ -1,29 +1,39 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.cli.plan_offer.adapters.fake_whoami_gateway import FakeWhoAmIGateway
|
||||
from vibe.cli.textual_ui.app import VibeApp
|
||||
from vibe.cli.textual_ui.widgets.messages import AssistantMessage, UserMessage
|
||||
from tests.conftest import (
|
||||
build_test_agent_loop,
|
||||
build_test_vibe_app,
|
||||
build_test_vibe_config,
|
||||
)
|
||||
from tests.update_notifier.adapters.fake_update_cache_repository import (
|
||||
FakeUpdateCacheRepository,
|
||||
)
|
||||
from tests.update_notifier.adapters.fake_update_gateway import FakeUpdateGateway
|
||||
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIResponse
|
||||
from vibe.cli.textual_ui.widgets.messages import (
|
||||
AssistantMessage,
|
||||
UserMessage,
|
||||
WhatsNewMessage,
|
||||
)
|
||||
from vibe.cli.textual_ui.widgets.tools import ToolCallMessage, ToolResultMessage
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
from vibe.cli.update_notifier import UpdateCache
|
||||
from vibe.core.config import VibeConfig
|
||||
from vibe.core.types import FunctionCall, LLMMessage, Role, ToolCall
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vibe_config() -> VibeConfig:
|
||||
return VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False), enable_update_checks=False
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_displays_messages_when_resuming_session(
|
||||
vibe_config: VibeConfig,
|
||||
) -> None:
|
||||
"""Test that messages are properly displayed when resuming a session."""
|
||||
agent_loop = AgentLoop(config=vibe_config, enable_streaming=False)
|
||||
agent_loop = build_test_agent_loop(config=vibe_config)
|
||||
|
||||
# Simulate a previous session with messages
|
||||
user_msg = LLMMessage(role=Role.user, content="Hello, how are you?")
|
||||
|
|
@ -49,7 +59,7 @@ async def test_ui_displays_messages_when_resuming_session(
|
|||
|
||||
agent_loop.messages.extend([user_msg, assistant_msg, tool_result_msg])
|
||||
|
||||
app = VibeApp(agent_loop=agent_loop, plan_offer_gateway=FakeWhoAmIGateway())
|
||||
app = build_test_vibe_app(agent_loop=agent_loop)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
# Wait for the app to initialize and rebuild history
|
||||
|
|
@ -82,13 +92,13 @@ async def test_ui_does_not_display_messages_when_only_system_messages_exist(
|
|||
vibe_config: VibeConfig,
|
||||
) -> None:
|
||||
"""Test that no messages are displayed when only system messages exist."""
|
||||
agent_loop = AgentLoop(config=vibe_config, enable_streaming=False)
|
||||
agent_loop = build_test_agent_loop(config=vibe_config)
|
||||
|
||||
# Only system messages
|
||||
system_msg = LLMMessage(role=Role.system, content="System prompt")
|
||||
agent_loop.messages.append(system_msg)
|
||||
|
||||
app = VibeApp(agent_loop=agent_loop, plan_offer_gateway=FakeWhoAmIGateway())
|
||||
app = build_test_vibe_app(agent_loop=agent_loop)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.5)
|
||||
|
|
@ -106,7 +116,7 @@ async def test_ui_displays_multiple_user_assistant_turns(
|
|||
vibe_config: VibeConfig,
|
||||
) -> None:
|
||||
"""Test that multiple conversation turns are properly displayed."""
|
||||
agent_loop = AgentLoop(config=vibe_config, enable_streaming=False)
|
||||
agent_loop = build_test_agent_loop(config=vibe_config)
|
||||
|
||||
# Multiple conversation turns
|
||||
messages = [
|
||||
|
|
@ -118,7 +128,7 @@ async def test_ui_displays_multiple_user_assistant_turns(
|
|||
|
||||
agent_loop.messages.extend(messages)
|
||||
|
||||
app = VibeApp(agent_loop=agent_loop, plan_offer_gateway=FakeWhoAmIGateway())
|
||||
app = build_test_vibe_app(agent_loop=agent_loop)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.5)
|
||||
|
|
@ -133,3 +143,55 @@ async def test_ui_displays_multiple_user_assistant_turns(
|
|||
assert len(assistant_messages) == 2
|
||||
assert assistant_messages[0]._content == "First answer"
|
||||
assert assistant_messages[1]._content == "Second answer"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_rebuilds_history_when_whats_new_is_shown(
|
||||
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
# we have to define an api key to make sure we display the Plan Offer message
|
||||
monkeypatch.setenv("MISTRAL_API_KEY", "api-key")
|
||||
config = build_test_vibe_config(enable_update_checks=True)
|
||||
agent_loop = build_test_agent_loop(config=config)
|
||||
agent_loop.messages.extend([
|
||||
LLMMessage(role=Role.user, content="Hello from the previous session."),
|
||||
LLMMessage(role=Role.assistant, content="Welcome back!"),
|
||||
])
|
||||
update_cache = UpdateCache(
|
||||
latest_version="1.0.0",
|
||||
stored_at_timestamp=int(time.time()),
|
||||
seen_whats_new_version=None,
|
||||
)
|
||||
update_cache_repository = FakeUpdateCacheRepository(update_cache=update_cache)
|
||||
plan_offer_gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=False,
|
||||
advertise_pro_plan=True,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
app = build_test_vibe_app(
|
||||
agent_loop=agent_loop,
|
||||
update_notifier=FakeUpdateGateway(update=None),
|
||||
update_cache_repository=update_cache_repository,
|
||||
plan_offer_gateway=plan_offer_gateway,
|
||||
current_version="1.0.0",
|
||||
config=config,
|
||||
)
|
||||
|
||||
with patch("vibe.cli.update_notifier.whats_new.VIBE_ROOT", tmp_path):
|
||||
whats_new_file = tmp_path / "whats_new.md"
|
||||
whats_new_file.write_text("# What's New\n\n- Feature 1")
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause(0.5)
|
||||
whats_new_message = app.query_one(WhatsNewMessage)
|
||||
message = app.query_one(UserMessage)
|
||||
assistant_message = app.query_one(AssistantMessage)
|
||||
messages_area = app.query_one("#messages")
|
||||
children = list(messages_area.children)
|
||||
|
||||
assert message._content == "Hello from the previous session."
|
||||
assert whats_new_message is not None
|
||||
assert "after-history" in whats_new_message.classes
|
||||
assert children == [message, assistant_message, whats_new_message]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,18 @@ from typing import Any
|
|||
import pytest
|
||||
import tomli_w
|
||||
|
||||
from tests.cli.plan_offer.adapters.fake_whoami_gateway import FakeWhoAmIGateway
|
||||
from tests.stubs.fake_backend import FakeBackend
|
||||
from tests.update_notifier.adapters.fake_update_cache_repository import (
|
||||
FakeUpdateCacheRepository,
|
||||
)
|
||||
from tests.update_notifier.adapters.fake_update_gateway import FakeUpdateGateway
|
||||
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIResponse
|
||||
from vibe.cli.textual_ui.app import CORE_VERSION, VibeApp
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.agents.models import BuiltinAgentName
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
from vibe.core.llm.types import BackendLike
|
||||
from vibe.core.paths import global_paths
|
||||
from vibe.core.paths.config_paths import unlock_config_paths
|
||||
|
||||
|
|
@ -80,3 +92,101 @@ def _mock_platform(monkeypatch: pytest.MonkeyPatch) -> None:
|
|||
@pytest.fixture(autouse=True)
|
||||
def _mock_update_commands(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr("vibe.cli.update_notifier.update.UPDATE_COMMANDS", ["true"])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vibe_app() -> VibeApp:
|
||||
return build_test_vibe_app()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def agent_loop() -> AgentLoop:
|
||||
return build_test_agent_loop()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vibe_config() -> VibeConfig:
|
||||
return build_test_vibe_config()
|
||||
|
||||
|
||||
def build_test_vibe_config(**kwargs) -> VibeConfig:
|
||||
session_logging = kwargs.pop("session_logging", None)
|
||||
resolved_session_logging = (
|
||||
SessionLoggingConfig(enabled=False)
|
||||
if session_logging is None
|
||||
else session_logging
|
||||
)
|
||||
enable_update_checks = kwargs.pop("enable_update_checks", None)
|
||||
resolved_enable_update_checks = (
|
||||
False if enable_update_checks is None else enable_update_checks
|
||||
)
|
||||
return VibeConfig(
|
||||
session_logging=resolved_session_logging,
|
||||
enable_update_checks=resolved_enable_update_checks,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def build_test_agent_loop(
|
||||
*,
|
||||
config: VibeConfig | None = None,
|
||||
agent_name: str = BuiltinAgentName.DEFAULT,
|
||||
backend: BackendLike | None = None,
|
||||
enable_streaming: bool = False,
|
||||
**kwargs,
|
||||
) -> AgentLoop:
|
||||
|
||||
resolved_config = config or build_test_vibe_config()
|
||||
|
||||
return AgentLoop(
|
||||
config=resolved_config,
|
||||
agent_name=agent_name,
|
||||
backend=backend or FakeBackend(),
|
||||
enable_streaming=enable_streaming,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def build_test_vibe_app(
|
||||
*, config: VibeConfig | None = None, agent_loop: AgentLoop | None = None, **kwargs
|
||||
) -> VibeApp:
|
||||
app_config = config or build_test_vibe_config()
|
||||
|
||||
resolved_agent_loop = agent_loop or build_test_agent_loop(config=app_config)
|
||||
|
||||
update_notifier = kwargs.pop("update_notifier", None)
|
||||
resolved_update_notifier = (
|
||||
FakeUpdateGateway() if update_notifier is None else update_notifier
|
||||
)
|
||||
update_cache_repository = kwargs.pop("update_cache_repository", None)
|
||||
resolved_update_cache_repository = (
|
||||
FakeUpdateCacheRepository()
|
||||
if update_cache_repository is None
|
||||
else update_cache_repository
|
||||
)
|
||||
plan_offer_gateway = kwargs.pop("plan_offer_gateway", None)
|
||||
resolved_plan_offer_gateway = (
|
||||
FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=True,
|
||||
advertise_pro_plan=False,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
if plan_offer_gateway is None
|
||||
else plan_offer_gateway
|
||||
)
|
||||
current_version = kwargs.pop("current_version", None)
|
||||
resolved_current_version = (
|
||||
CORE_VERSION if current_version is None else current_version
|
||||
)
|
||||
|
||||
return VibeApp(
|
||||
agent_loop=resolved_agent_loop,
|
||||
current_version=resolved_current_version,
|
||||
update_notifier=resolved_update_notifier,
|
||||
update_cache_repository=resolved_update_cache_repository,
|
||||
plan_offer_gateway=resolved_plan_offer_gateway,
|
||||
initial_prompt=kwargs.pop("initial_prompt", None),
|
||||
**kwargs,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from vibe.core.agents.manager import AgentManager
|
||||
from vibe.core.agents.models import BUILTIN_AGENTS, EXPLORE, AgentSafety, AgentType
|
||||
from vibe.core.config import VibeConfig
|
||||
|
||||
|
||||
class TestAgentProfile:
|
||||
|
|
@ -31,7 +31,9 @@ class TestAgentProfile:
|
|||
class TestAgentManager:
|
||||
@pytest.fixture
|
||||
def manager(self) -> AgentManager:
|
||||
config = VibeConfig(include_project_context=False, include_prompt_detail=False)
|
||||
config = build_test_vibe_config(
|
||||
include_project_context=False, include_prompt_detail=False
|
||||
)
|
||||
return AgentManager(lambda: config)
|
||||
|
||||
def test_get_subagents_returns_only_subagents(self, manager: AgentManager) -> None:
|
||||
|
|
|
|||
46
tests/core/test_auth_crypto.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict
|
||||
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
|
||||
from vibe.core.auth import EncryptedPayload, decrypt, encrypt
|
||||
|
||||
|
||||
def _generate_test_key_pair() -> tuple[bytes, bytes]:
|
||||
private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
|
||||
private_pem = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption(),
|
||||
)
|
||||
public_pem = private_key.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
return private_pem, public_pem
|
||||
|
||||
|
||||
class TestEncryptDecrypt:
|
||||
def test_encrypt_decrypt_roundtrip(self) -> None:
|
||||
private_pem, public_pem = _generate_test_key_pair()
|
||||
|
||||
plaintext = "ghp_test_token_12345"
|
||||
encrypted = encrypt(plaintext, public_pem)
|
||||
|
||||
assert encrypted.encrypted_key != plaintext
|
||||
assert encrypted.ciphertext != plaintext
|
||||
|
||||
decrypted = decrypt(encrypted, private_pem)
|
||||
assert decrypted == plaintext
|
||||
|
||||
def test_encrypted_payload_serialization(self) -> None:
|
||||
payload = EncryptedPayload(
|
||||
encrypted_key="enc_key", nonce="nonce123", ciphertext="cipher"
|
||||
)
|
||||
|
||||
data = asdict(payload)
|
||||
restored = EncryptedPayload(**data)
|
||||
|
||||
assert restored == payload
|
||||
286
tests/core/test_auth_github.py
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from vibe.core.auth.github import (
|
||||
DeviceFlowHandle,
|
||||
DeviceFlowInfo,
|
||||
GitHubAuthError,
|
||||
GitHubAuthProvider,
|
||||
)
|
||||
|
||||
|
||||
class TestDeviceFlowModels:
|
||||
def test_device_flow_info(self) -> None:
|
||||
info = DeviceFlowInfo(
|
||||
user_code="ABC-123", verification_uri="https://example.com"
|
||||
)
|
||||
assert info.user_code == "ABC-123"
|
||||
assert info.verification_uri == "https://example.com"
|
||||
|
||||
def test_device_flow_handle(self) -> None:
|
||||
info = DeviceFlowInfo(
|
||||
user_code="ABC-123", verification_uri="https://example.com"
|
||||
)
|
||||
handle = DeviceFlowHandle(device_code="dc_123", expires_in=900, info=info)
|
||||
assert handle.device_code == "dc_123"
|
||||
assert handle.expires_in == 900
|
||||
assert handle.info.user_code == "ABC-123"
|
||||
|
||||
|
||||
class TestGitHubAuthProviderContextManager:
|
||||
@pytest.mark.asyncio
|
||||
async def test_creates_client_on_enter(self) -> None:
|
||||
provider = GitHubAuthProvider()
|
||||
assert provider._client is None
|
||||
async with provider:
|
||||
assert provider._client is not None
|
||||
assert provider._client is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uses_provided_client(self) -> None:
|
||||
external_client = httpx.AsyncClient()
|
||||
provider = GitHubAuthProvider(client=external_client)
|
||||
async with provider:
|
||||
assert provider._client is external_client
|
||||
assert provider._client is external_client
|
||||
await external_client.aclose()
|
||||
|
||||
|
||||
class TestGitHubAuthProviderGetToken:
|
||||
def test_returns_token_from_keyring(self) -> None:
|
||||
with patch("vibe.core.auth.github.keyring") as mock_keyring:
|
||||
mock_keyring.get_password.return_value = "ghp_test_token"
|
||||
provider = GitHubAuthProvider()
|
||||
token = provider.get_token()
|
||||
assert token == "ghp_test_token"
|
||||
|
||||
def test_returns_none_on_keyring_error(self) -> None:
|
||||
with patch("vibe.core.auth.github.keyring") as mock_keyring:
|
||||
import keyring.errors
|
||||
|
||||
mock_keyring.errors = keyring.errors
|
||||
mock_keyring.get_password.side_effect = keyring.errors.KeyringError("error")
|
||||
provider = GitHubAuthProvider()
|
||||
token = provider.get_token()
|
||||
assert token is None
|
||||
|
||||
def test_returns_none_when_no_token(self) -> None:
|
||||
with patch("vibe.core.auth.github.keyring") as mock_keyring:
|
||||
mock_keyring.get_password.return_value = None
|
||||
provider = GitHubAuthProvider()
|
||||
token = provider.get_token()
|
||||
assert token is None
|
||||
|
||||
|
||||
class TestGitHubAuthProviderHasToken:
|
||||
def test_returns_true_when_token_exists(self) -> None:
|
||||
with patch("vibe.core.auth.github.keyring") as mock_keyring:
|
||||
mock_keyring.get_password.return_value = "ghp_token"
|
||||
provider = GitHubAuthProvider()
|
||||
assert provider.has_token() is True
|
||||
|
||||
def test_returns_false_when_no_token(self) -> None:
|
||||
with patch("vibe.core.auth.github.keyring") as mock_keyring:
|
||||
mock_keyring.get_password.return_value = None
|
||||
provider = GitHubAuthProvider()
|
||||
assert provider.has_token() is False
|
||||
|
||||
|
||||
class TestGitHubAuthProviderStartDeviceFlow:
|
||||
@pytest.fixture
|
||||
def mock_client(self) -> MagicMock:
|
||||
return MagicMock(spec=httpx.AsyncClient)
|
||||
|
||||
@pytest.fixture
|
||||
def provider(self, mock_client: MagicMock) -> GitHubAuthProvider:
|
||||
return GitHubAuthProvider(client=mock_client)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_device_flow_success(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = True
|
||||
mock_response.json.return_value = {
|
||||
"device_code": "dc_123",
|
||||
"user_code": "ABC-123",
|
||||
"verification_uri": "https://github.com/login/device",
|
||||
"expires_in": 900,
|
||||
}
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
with patch("vibe.core.auth.github.webbrowser") as mock_browser:
|
||||
handle = await provider.start_device_flow(open_browser=True)
|
||||
mock_browser.open.assert_called_once_with("https://github.com/login/device")
|
||||
|
||||
assert handle.device_code == "dc_123"
|
||||
assert handle.info.user_code == "ABC-123"
|
||||
assert handle.expires_in == 900
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_device_flow_without_browser(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = True
|
||||
mock_response.json.return_value = {
|
||||
"device_code": "dc_123",
|
||||
"user_code": "ABC-123",
|
||||
"verification_uri": "https://github.com/login/device",
|
||||
"expires_in": 900,
|
||||
}
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
with patch("vibe.core.auth.github.webbrowser") as mock_browser:
|
||||
await provider.start_device_flow(open_browser=False)
|
||||
mock_browser.open.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_device_flow_failure(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = False
|
||||
mock_response.text = "Bad request"
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
with pytest.raises(GitHubAuthError, match="Failed to initiate device flow"):
|
||||
await provider.start_device_flow()
|
||||
|
||||
|
||||
class TestGitHubAuthProviderPollForToken:
|
||||
@pytest.fixture
|
||||
def mock_client(self) -> MagicMock:
|
||||
return MagicMock(spec=httpx.AsyncClient)
|
||||
|
||||
@pytest.fixture
|
||||
def provider(self, mock_client: MagicMock) -> GitHubAuthProvider:
|
||||
return GitHubAuthProvider(client=mock_client)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_poll_returns_token_on_success(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"access_token": "ghp_new_token"}
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
with patch("vibe.core.auth.github.asyncio.sleep", new_callable=AsyncMock):
|
||||
token = await provider._poll_for_token(
|
||||
mock_client, "dc_123", expires_in=10, interval=1
|
||||
)
|
||||
assert token == "ghp_new_token"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_poll_handles_slow_down(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
responses = [
|
||||
MagicMock(
|
||||
json=MagicMock(return_value={"error": "slow_down", "interval": 5})
|
||||
),
|
||||
MagicMock(json=MagicMock(return_value={"access_token": "ghp_token"})),
|
||||
]
|
||||
mock_client.post = AsyncMock(side_effect=responses)
|
||||
|
||||
with patch("vibe.core.auth.github.asyncio.sleep", new_callable=AsyncMock):
|
||||
token = await provider._poll_for_token(
|
||||
mock_client, "dc_123", expires_in=30, interval=1
|
||||
)
|
||||
assert token == "ghp_token"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_poll_raises_on_expired_token(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"error": "expired_token"}
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
with patch("vibe.core.auth.github.asyncio.sleep", new_callable=AsyncMock):
|
||||
with pytest.raises(GitHubAuthError, match="expired_token"):
|
||||
await provider._poll_for_token(
|
||||
mock_client, "dc_123", expires_in=10, interval=1
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_poll_raises_on_access_denied(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"error": "access_denied"}
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
with patch("vibe.core.auth.github.asyncio.sleep", new_callable=AsyncMock):
|
||||
with pytest.raises(GitHubAuthError, match="access_denied"):
|
||||
await provider._poll_for_token(
|
||||
mock_client, "dc_123", expires_in=10, interval=1
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_poll_raises_on_timeout(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"error": "authorization_pending"}
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
with patch("vibe.core.auth.github.asyncio.sleep", new_callable=AsyncMock):
|
||||
with pytest.raises(GitHubAuthError, match="timed out"):
|
||||
await provider._poll_for_token(
|
||||
mock_client, "dc_123", expires_in=2, interval=1
|
||||
)
|
||||
|
||||
|
||||
class TestGitHubAuthProviderSaveToken:
|
||||
def test_save_token_success(self) -> None:
|
||||
with patch("vibe.core.auth.github.keyring") as mock_keyring:
|
||||
provider = GitHubAuthProvider()
|
||||
provider._save_token("ghp_token")
|
||||
mock_keyring.set_password.assert_called_once_with(
|
||||
"vibe", "github_token", "ghp_token"
|
||||
)
|
||||
|
||||
def test_save_token_raises_on_keyring_error(self) -> None:
|
||||
with patch("vibe.core.auth.github.keyring") as mock_keyring:
|
||||
import keyring.errors
|
||||
|
||||
mock_keyring.errors = keyring.errors
|
||||
mock_keyring.set_password.side_effect = keyring.errors.KeyringError("error")
|
||||
provider = GitHubAuthProvider()
|
||||
with pytest.raises(GitHubAuthError, match="Failed to save token"):
|
||||
provider._save_token("ghp_token")
|
||||
|
||||
|
||||
class TestGitHubAuthProviderWaitForToken:
|
||||
@pytest.fixture
|
||||
def mock_client(self) -> MagicMock:
|
||||
return MagicMock(spec=httpx.AsyncClient)
|
||||
|
||||
@pytest.fixture
|
||||
def provider(self, mock_client: MagicMock) -> GitHubAuthProvider:
|
||||
return GitHubAuthProvider(client=mock_client)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wait_for_token_polls_and_saves(
|
||||
self, provider: GitHubAuthProvider, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {"access_token": "ghp_token"}
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
info = DeviceFlowInfo(user_code="ABC", verification_uri="https://example.com")
|
||||
handle = DeviceFlowHandle(device_code="dc_123", expires_in=10, info=info)
|
||||
|
||||
with (
|
||||
patch("vibe.core.auth.github.asyncio.sleep", new_callable=AsyncMock),
|
||||
patch("vibe.core.auth.github.keyring") as mock_keyring,
|
||||
):
|
||||
token = await provider.wait_for_token(handle)
|
||||
|
||||
assert token == "ghp_token"
|
||||
mock_keyring.set_password.assert_called_once()
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import threading
|
||||
|
||||
from vibe.core.config import load_dotenv_values
|
||||
|
||||
|
|
@ -59,3 +61,21 @@ def test_ignores_empty_values(tmp_path: Path) -> None:
|
|||
assert "EMPTY" not in environ
|
||||
assert "MISTRAL_API_KEY" not in environ
|
||||
assert "NO_VALUE" not in environ
|
||||
|
||||
|
||||
def test_reads_from_fifo(tmp_path: Path) -> None:
|
||||
fifo_path = tmp_path / ".env.fifo"
|
||||
os.mkfifo(fifo_path)
|
||||
environ: dict[str, str] = {}
|
||||
|
||||
def write_to_fifo() -> None:
|
||||
with open(fifo_path, "w") as f:
|
||||
f.write("FIFO_KEY=fifo-value\n")
|
||||
|
||||
writer = threading.Thread(target=write_to_fifo)
|
||||
writer.start()
|
||||
|
||||
load_dotenv_values(env_path=fifo_path, environ=environ)
|
||||
|
||||
writer.join()
|
||||
assert environ["FIFO_KEY"] == "fifo-value"
|
||||
|
|
|
|||
327
tests/core/test_teleport_git.py
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from vibe.core.teleport.errors import (
|
||||
ServiceTeleportError,
|
||||
ServiceTeleportNotSupportedError,
|
||||
)
|
||||
from vibe.core.teleport.git import GitRepoInfo, GitRepository
|
||||
|
||||
|
||||
def make_mock_remote(url: str) -> MagicMock:
|
||||
remote = MagicMock()
|
||||
remote.urls = [url]
|
||||
return remote
|
||||
|
||||
|
||||
def make_mock_repo(
|
||||
urls: list[str] | None = None,
|
||||
commit: str | None = "abc123",
|
||||
branch: str | None = "main",
|
||||
is_detached: bool = False,
|
||||
diff: str = "",
|
||||
) -> MagicMock:
|
||||
mock = MagicMock()
|
||||
if urls:
|
||||
mock.remotes = [make_mock_remote(url) for url in urls]
|
||||
else:
|
||||
mock.remotes = []
|
||||
mock.head.commit.hexsha = commit
|
||||
mock.head.is_detached = is_detached
|
||||
mock.active_branch.name = branch
|
||||
mock.git.diff.return_value = diff
|
||||
mock.git.branch.return_value = ""
|
||||
mock.git.rev_list.return_value = "0"
|
||||
mock.git.rev_parse.return_value = "abc123"
|
||||
mock.remote.return_value = make_mock_remote(urls[0] if urls else "")
|
||||
return mock
|
||||
|
||||
|
||||
class TestGitRepositoryParseGithubUrl:
|
||||
def test_parse_ssh_url(self) -> None:
|
||||
result = GitRepository._parse_github_url("git@github.com:owner/repo.git")
|
||||
assert result == ("owner", "repo")
|
||||
|
||||
def test_parse_ssh_url_without_git_suffix(self) -> None:
|
||||
result = GitRepository._parse_github_url("git@github.com:owner/repo")
|
||||
assert result == ("owner", "repo")
|
||||
|
||||
def test_parse_https_url(self) -> None:
|
||||
result = GitRepository._parse_github_url("https://github.com/owner/repo.git")
|
||||
assert result == ("owner", "repo")
|
||||
|
||||
def test_parse_https_url_without_git_suffix(self) -> None:
|
||||
result = GitRepository._parse_github_url("https://github.com/owner/repo")
|
||||
assert result == ("owner", "repo")
|
||||
|
||||
def test_parse_https_url_with_credentials(self) -> None:
|
||||
result = GitRepository._parse_github_url(
|
||||
"https://x-access-token:gho_xxxx@github.com/owner/repo.git"
|
||||
)
|
||||
assert result == ("owner", "repo")
|
||||
|
||||
def test_parse_non_github_url_returns_none(self) -> None:
|
||||
result = GitRepository._parse_github_url("git@gitlab.com:owner/repo.git")
|
||||
assert result is None
|
||||
|
||||
def test_parse_invalid_url_returns_none(self) -> None:
|
||||
result = GitRepository._parse_github_url("not-a-valid-url")
|
||||
assert result is None
|
||||
|
||||
|
||||
class TestGitRepositoryToHttpsUrl:
|
||||
def test_converts_to_https_url(self) -> None:
|
||||
result = GitRepository._to_https_url("owner", "repo")
|
||||
assert result == "https://github.com/owner/repo.git"
|
||||
|
||||
|
||||
class TestGitRepositoryIsSupported:
|
||||
@pytest.fixture
|
||||
def repo(self, tmp_path: Path) -> GitRepository:
|
||||
return GitRepository(tmp_path)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_false_when_not_git_repo(self, tmp_path: Path) -> None:
|
||||
repo = GitRepository(tmp_path)
|
||||
assert await repo.is_supported() is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_false_when_no_remote(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=None)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.is_supported() is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_false_when_non_github_remote(
|
||||
self, repo: GitRepository
|
||||
) -> None:
|
||||
mock = make_mock_repo(urls=["git@gitlab.com:owner/repo.git"])
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.is_supported() is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_true_when_github_repo(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"])
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.is_supported() is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_finds_github_among_multiple_remotes(
|
||||
self, repo: GitRepository
|
||||
) -> None:
|
||||
mock = make_mock_repo(
|
||||
urls=["git@gitlab.com:owner/repo.git", "git@github.com:owner/repo.git"]
|
||||
)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.is_supported() is True
|
||||
|
||||
|
||||
class TestGitRepositoryGetInfo:
|
||||
@pytest.fixture
|
||||
def repo(self, tmp_path: Path) -> GitRepository:
|
||||
return GitRepository(tmp_path)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_raises_when_not_git_repo(self, tmp_path: Path) -> None:
|
||||
repo = GitRepository(tmp_path)
|
||||
with pytest.raises(
|
||||
ServiceTeleportNotSupportedError, match="Not a git repository"
|
||||
):
|
||||
await repo.get_info()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_raises_when_no_remote(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=None)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
with pytest.raises(
|
||||
ServiceTeleportNotSupportedError, match="No GitHub remote"
|
||||
):
|
||||
await repo.get_info()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_raises_when_non_github_remote(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@gitlab.com:owner/repo.git"])
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
with pytest.raises(
|
||||
ServiceTeleportNotSupportedError, match="No GitHub remote"
|
||||
):
|
||||
await repo.get_info()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_raises_when_no_commit(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"], commit=None)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
with pytest.raises(
|
||||
ServiceTeleportNotSupportedError,
|
||||
match="Could not determine current commit",
|
||||
):
|
||||
await repo.get_info()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_info_on_success(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(
|
||||
urls=["git@github.com:owner/repo.git"],
|
||||
commit="abc123def456",
|
||||
branch="main",
|
||||
diff="diff content",
|
||||
)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
info = await repo.get_info()
|
||||
assert info == GitRepoInfo(
|
||||
remote_url="https://github.com/owner/repo.git",
|
||||
owner="owner",
|
||||
repo="repo",
|
||||
branch="main",
|
||||
commit="abc123def456",
|
||||
diff="diff content",
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handles_detached_head(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(
|
||||
urls=["git@github.com:owner/repo.git"],
|
||||
commit="abc123def456",
|
||||
is_detached=True,
|
||||
)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
info = await repo.get_info()
|
||||
assert info.branch is None
|
||||
|
||||
|
||||
class TestGitRepositoryIsCommitPushed:
|
||||
@pytest.fixture
|
||||
def repo(self, tmp_path: Path) -> GitRepository:
|
||||
return GitRepository(tmp_path)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_false_when_not_on_remote(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"])
|
||||
mock.git.branch.return_value = ""
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.is_commit_pushed("abc123") is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_true_when_on_remote(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"])
|
||||
mock.git.branch.return_value = " origin/main\n origin/feature"
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.is_commit_pushed("abc123") is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_checks_correct_remote(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"])
|
||||
mock.git.branch.return_value = " upstream/main\n"
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.is_commit_pushed("abc123", remote="upstream") is True
|
||||
assert await repo.is_commit_pushed("abc123", remote="origin") is False
|
||||
|
||||
|
||||
class TestGitRepositoryGetUnpushedCommitCount:
|
||||
@pytest.fixture
|
||||
def repo(self, tmp_path: Path) -> GitRepository:
|
||||
return GitRepository(tmp_path)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_raises_when_no_branch(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"], is_detached=True)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
with pytest.raises(ServiceTeleportError, match="no current branch"):
|
||||
await repo.get_unpushed_commit_count()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_count(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"], branch="main")
|
||||
mock.git.rev_list.return_value = "5"
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.get_unpushed_commit_count() == 5
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fallback_to_default_branch(self, repo: GitRepository) -> None:
|
||||
from git.exc import GitCommandError
|
||||
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"], branch="feature")
|
||||
|
||||
def rev_list_side_effect(*args: object, **kwargs: object) -> str:
|
||||
if "origin/feature..HEAD" in args:
|
||||
raise GitCommandError("git rev-list", 128)
|
||||
if "origin/main..HEAD" in args:
|
||||
return "3"
|
||||
return "0"
|
||||
|
||||
mock.git.rev_list.side_effect = rev_list_side_effect
|
||||
with (
|
||||
patch.object(repo, "_repo_or_raise", return_value=mock),
|
||||
patch.object(
|
||||
repo,
|
||||
"_get_remote_default_branch",
|
||||
AsyncMock(return_value="origin/main"),
|
||||
),
|
||||
):
|
||||
assert await repo.get_unpushed_commit_count() == 3
|
||||
|
||||
|
||||
class TestGitRepositoryPushCurrentBranch:
|
||||
@pytest.fixture
|
||||
def repo(self, tmp_path: Path) -> GitRepository:
|
||||
return GitRepository(tmp_path)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_false_when_no_branch(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"], is_detached=True)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.push_current_branch() is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_false_when_push_fails(self, repo: GitRepository) -> None:
|
||||
from git.exc import GitCommandError
|
||||
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"], branch="main")
|
||||
mock.remote.return_value.push.side_effect = GitCommandError("git push", 1)
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.push_current_branch() is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_true_when_push_succeeds(self, repo: GitRepository) -> None:
|
||||
mock = make_mock_repo(urls=["git@github.com:owner/repo.git"], branch="main")
|
||||
with patch.object(repo, "_repo_or_raise", return_value=mock):
|
||||
assert await repo.push_current_branch() is True
|
||||
|
||||
|
||||
class TestGitRepositoryGetRemoteDefaultBranch:
|
||||
@pytest.fixture
|
||||
def repo(self, tmp_path: Path) -> GitRepository:
|
||||
return GitRepository(tmp_path)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_default_branch_when_head_exists(
|
||||
self, repo: GitRepository
|
||||
) -> None:
|
||||
mock = MagicMock()
|
||||
mock_head_ref = MagicMock()
|
||||
mock_head_ref.reference.name = "origin/main"
|
||||
mock.remotes.__getitem__.return_value.refs.HEAD = mock_head_ref
|
||||
mock.git.rev_parse.return_value = "abc123"
|
||||
result = await repo._get_remote_default_branch(mock, "origin")
|
||||
assert result == "origin/main"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_none_when_remote_not_found(
|
||||
self, repo: GitRepository
|
||||
) -> None:
|
||||
mock = MagicMock()
|
||||
mock.remotes.__getitem__.side_effect = IndexError("No item found")
|
||||
result = await repo._get_remote_default_branch(mock, "upstream")
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_returns_none_when_head_ref_missing(
|
||||
self, repo: GitRepository
|
||||
) -> None:
|
||||
mock = MagicMock()
|
||||
mock.remotes.__getitem__.return_value.refs.HEAD = None
|
||||
result = await repo._get_remote_default_branch(mock, "origin")
|
||||
assert result is None
|
||||
337
tests/core/test_teleport_nuage.py
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from vibe.core.auth import EncryptedPayload
|
||||
from vibe.core.teleport.errors import ServiceTeleportError
|
||||
from vibe.core.teleport.nuage import (
|
||||
CreateLeChatThreadInput,
|
||||
GitRepoConfig,
|
||||
NuageClient,
|
||||
VibeNewSandbox,
|
||||
VibeSandboxConfig,
|
||||
WorkflowParams,
|
||||
)
|
||||
|
||||
|
||||
class TestNuageModels:
|
||||
def test_git_repo_config_defaults(self) -> None:
|
||||
config = GitRepoConfig(url="https://github.com/owner/repo.git")
|
||||
assert config.url == "https://github.com/owner/repo.git"
|
||||
assert config.branch is None
|
||||
assert config.commit is None
|
||||
|
||||
def test_git_repo_config_with_values(self) -> None:
|
||||
config = GitRepoConfig(
|
||||
url="https://github.com/owner/repo.git", branch="main", commit="abc123"
|
||||
)
|
||||
assert config.branch == "main"
|
||||
assert config.commit == "abc123"
|
||||
|
||||
def test_vibe_sandbox_config_defaults(self) -> None:
|
||||
config = VibeSandboxConfig()
|
||||
assert config.git_repo is None
|
||||
|
||||
def test_vibe_new_sandbox_defaults(self) -> None:
|
||||
sandbox = VibeNewSandbox()
|
||||
assert sandbox.type == "new"
|
||||
assert sandbox.config.git_repo is None
|
||||
assert sandbox.teleported_diffs is None
|
||||
|
||||
def test_workflow_params_serialization(self) -> None:
|
||||
params = WorkflowParams(
|
||||
prompt="test prompt",
|
||||
sandbox=VibeNewSandbox(
|
||||
config=VibeSandboxConfig(
|
||||
git_repo=GitRepoConfig(
|
||||
url="https://github.com/owner/repo.git",
|
||||
branch="main",
|
||||
commit="abc123",
|
||||
)
|
||||
),
|
||||
teleported_diffs=b"base64data",
|
||||
),
|
||||
)
|
||||
data = params.model_dump()
|
||||
assert data["prompt"] == "test prompt"
|
||||
assert data["sandbox"]["type"] == "new"
|
||||
assert data["sandbox"]["config"]["git_repo"]["url"] == (
|
||||
"https://github.com/owner/repo.git"
|
||||
)
|
||||
assert data["sandbox"]["teleported_diffs"] == b"base64data"
|
||||
|
||||
def test_create_le_chat_thread_input(self) -> None:
|
||||
input_data = CreateLeChatThreadInput(
|
||||
encrypted_api_key={"key": "value"},
|
||||
user_message="test message",
|
||||
project_name="test-project",
|
||||
)
|
||||
assert input_data.encrypted_api_key == {"key": "value"}
|
||||
assert input_data.user_message == "test message"
|
||||
assert input_data.project_name == "test-project"
|
||||
|
||||
|
||||
class TestNuageClientContextManager:
|
||||
@pytest.mark.asyncio
|
||||
async def test_creates_client_on_enter(self) -> None:
|
||||
nuage = NuageClient("https://api.example.com", "api-key", "workflow-id")
|
||||
assert nuage._client is None
|
||||
async with nuage:
|
||||
assert nuage._client is not None
|
||||
assert nuage._client is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uses_provided_client(self) -> None:
|
||||
external_client = httpx.AsyncClient()
|
||||
nuage = NuageClient(
|
||||
"https://api.example.com", "api-key", "workflow-id", client=external_client
|
||||
)
|
||||
async with nuage:
|
||||
assert nuage._client is external_client
|
||||
assert nuage._client is external_client
|
||||
await external_client.aclose()
|
||||
|
||||
|
||||
class TestNuageClientStartWorkflow:
|
||||
@pytest.fixture
|
||||
def mock_client(self) -> MagicMock:
|
||||
client = MagicMock(spec=httpx.AsyncClient)
|
||||
return client
|
||||
|
||||
@pytest.fixture
|
||||
def nuage(self, mock_client: MagicMock) -> NuageClient:
|
||||
return NuageClient(
|
||||
"https://api.example.com", "api-key", "workflow-id", client=mock_client
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_workflow_success(
|
||||
self, nuage: NuageClient, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = True
|
||||
mock_response.json.return_value = {"execution_id": "exec-123"}
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
params = WorkflowParams(prompt="test", sandbox=VibeNewSandbox())
|
||||
execution_id = await nuage.start_workflow(params)
|
||||
|
||||
assert execution_id == "exec-123"
|
||||
mock_client.post.assert_called_once()
|
||||
call_args = mock_client.post.call_args
|
||||
assert "workflows/workflow-id/execute" in call_args[0][0]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_workflow_failure(
|
||||
self, nuage: NuageClient, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = False
|
||||
mock_response.text = "Internal Server Error"
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
params = WorkflowParams(prompt="test", sandbox=VibeNewSandbox())
|
||||
with pytest.raises(ServiceTeleportError, match="Nuage workflow trigger failed"):
|
||||
await nuage.start_workflow(params)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_workflow_unauthorized_hint(
|
||||
self, nuage: NuageClient, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = False
|
||||
mock_response.text = "Unauthorized"
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
params = WorkflowParams(prompt="test", sandbox=VibeNewSandbox())
|
||||
with pytest.raises(ServiceTeleportError, match="STAGING_MISTRAL_API_KEY"):
|
||||
await nuage.start_workflow(params)
|
||||
|
||||
|
||||
class TestNuageClientSendGithubToken:
|
||||
@pytest.fixture
|
||||
def mock_client(self) -> MagicMock:
|
||||
return MagicMock(spec=httpx.AsyncClient)
|
||||
|
||||
@pytest.fixture
|
||||
def nuage(self, mock_client: MagicMock) -> NuageClient:
|
||||
return NuageClient(
|
||||
"https://api.example.com", "api-key", "workflow-id", client=mock_client
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_github_token_success(
|
||||
self,
|
||||
nuage: NuageClient,
|
||||
mock_client: MagicMock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
query_response = MagicMock()
|
||||
query_response.is_success = True
|
||||
query_response.json.return_value = {
|
||||
"result": {
|
||||
"public_key": (
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Z3VS5JJcds3xfn/"
|
||||
"ygWyf8TFXQNZ0XsLOqXB1Mi2+bKPFv1WfhECTxJ3c6SXl0p1sGyWTFxRV8u/"
|
||||
"bKqYZ0E6VZ6YRTRPFiGq0kkONjVBFxOQ8Y0jeT0d9e0Y3E3MWDL8tQ0Nz9v8"
|
||||
"5Y7gC8F1m/dEbBwPjCJQV0Dg0z3gZDO8RCG0GrBoLO0b+NNqL8FXPPDXQ1l4"
|
||||
"FGnYM0gZ1rCU7Y/zTN1wI4sCQ0GJQPDA1hWB8KRJl5x0ZDXE3rRwT1E8c+Fn"
|
||||
"ZFV1nN0C6zxF7GpVY3FVWXS4PA0FH+8C1+TnYgBL7xS0o+LF6PgjGT5F3CXD"
|
||||
"BZmYSxKL+EsVVGT5EuYbJE9TxVwIDAQAB\n"
|
||||
"-----END PUBLIC KEY-----"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
signal_response = MagicMock()
|
||||
signal_response.is_success = True
|
||||
|
||||
mock_client.post = AsyncMock(side_effect=[query_response, signal_response])
|
||||
|
||||
encrypted = EncryptedPayload(
|
||||
encrypted_key="enc_key", nonce="nonce", ciphertext="cipher"
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"vibe.core.teleport.nuage.encrypt", lambda _token, _key: encrypted
|
||||
)
|
||||
|
||||
await nuage.send_github_token("exec-123", "ghp_token")
|
||||
|
||||
assert mock_client.post.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_public_key_failure(
|
||||
self, nuage: NuageClient, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = False
|
||||
mock_response.text = "Not found"
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
with pytest.raises(ServiceTeleportError, match="Failed to get public key"):
|
||||
await nuage._query_public_key("exec-123")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_signal_encrypted_token_failure(
|
||||
self, nuage: NuageClient, mock_client: MagicMock
|
||||
) -> None:
|
||||
mock_response = MagicMock()
|
||||
mock_response.is_success = False
|
||||
mock_response.text = "Signal failed"
|
||||
mock_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
encrypted = EncryptedPayload(
|
||||
encrypted_key="enc_key", nonce="nonce", ciphertext="cipher"
|
||||
)
|
||||
with pytest.raises(ServiceTeleportError, match="Failed to send GitHub token"):
|
||||
await nuage._signal_encrypted_token("exec-123", encrypted)
|
||||
|
||||
|
||||
class TestNuageClientCreateLeChatThread:
|
||||
@pytest.fixture
|
||||
def mock_client(self) -> MagicMock:
|
||||
return MagicMock(spec=httpx.AsyncClient)
|
||||
|
||||
@pytest.fixture
|
||||
def nuage(self, mock_client: MagicMock) -> NuageClient:
|
||||
return NuageClient(
|
||||
"https://api.example.com", "api-key", "workflow-id", client=mock_client
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_le_chat_thread_success(
|
||||
self,
|
||||
nuage: NuageClient,
|
||||
mock_client: MagicMock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
query_response = MagicMock()
|
||||
query_response.is_success = True
|
||||
query_response.json.return_value = {
|
||||
"result": {
|
||||
"public_key": (
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Z3VS5JJcds3xfn/"
|
||||
"ygWyf8TFXQNZ0XsLOqXB1Mi2+bKPFv1WfhECTxJ3c6SXl0p1sGyWTFxRV8u/"
|
||||
"bKqYZ0E6VZ6YRTRPFiGq0kkONjVBFxOQ8Y0jeT0d9e0Y3E3MWDL8tQ0Nz9v8"
|
||||
"5Y7gC8F1m/dEbBwPjCJQV0Dg0z3gZDO8RCG0GrBoLO0b+NNqL8FXPPDXQ1l4"
|
||||
"FGnYM0gZ1rCU7Y/zTN1wI4sCQ0GJQPDA1hWB8KRJl5x0ZDXE3rRwT1E8c+Fn"
|
||||
"ZFV1nN0C6zxF7GpVY3FVWXS4PA0FH+8C1+TnYgBL7xS0o+LF6PgjGT5F3CXD"
|
||||
"BZmYSxKL+EsVVGT5EuYbJE9TxVwIDAQAB\n"
|
||||
"-----END PUBLIC KEY-----"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
update_response = MagicMock()
|
||||
update_response.is_success = True
|
||||
update_response.json.return_value = {
|
||||
"result": {"chat_url": "https://chat.example.com/thread/123"}
|
||||
}
|
||||
|
||||
mock_client.post = AsyncMock(side_effect=[query_response, update_response])
|
||||
|
||||
encrypted = EncryptedPayload(
|
||||
encrypted_key="enc_key", nonce="nonce", ciphertext="cipher"
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"vibe.core.teleport.nuage.encrypt", lambda _token, _key: encrypted
|
||||
)
|
||||
|
||||
url = await nuage.create_le_chat_thread("exec-123", "test message")
|
||||
assert url == "https://chat.example.com/thread/123"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_le_chat_thread_failure(
|
||||
self,
|
||||
nuage: NuageClient,
|
||||
mock_client: MagicMock,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
query_response = MagicMock()
|
||||
query_response.is_success = True
|
||||
query_response.json.return_value = {
|
||||
"result": {
|
||||
"public_key": (
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Z3VS5JJcds3xfn/"
|
||||
"ygWyf8TFXQNZ0XsLOqXB1Mi2+bKPFv1WfhECTxJ3c6SXl0p1sGyWTFxRV8u/"
|
||||
"bKqYZ0E6VZ6YRTRPFiGq0kkONjVBFxOQ8Y0jeT0d9e0Y3E3MWDL8tQ0Nz9v8"
|
||||
"5Y7gC8F1m/dEbBwPjCJQV0Dg0z3gZDO8RCG0GrBoLO0b+NNqL8FXPPDXQ1l4"
|
||||
"FGnYM0gZ1rCU7Y/zTN1wI4sCQ0GJQPDA1hWB8KRJl5x0ZDXE3rRwT1E8c+Fn"
|
||||
"ZFV1nN0C6zxF7GpVY3FVWXS4PA0FH+8C1+TnYgBL7xS0o+LF6PgjGT5F3CXD"
|
||||
"BZmYSxKL+EsVVGT5EuYbJE9TxVwIDAQAB\n"
|
||||
"-----END PUBLIC KEY-----"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
update_response = MagicMock()
|
||||
update_response.is_success = False
|
||||
update_response.text = "Failed to create thread"
|
||||
|
||||
mock_client.post = AsyncMock(side_effect=[query_response, update_response])
|
||||
|
||||
encrypted = EncryptedPayload(
|
||||
encrypted_key="enc_key", nonce="nonce", ciphertext="cipher"
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"vibe.core.teleport.nuage.encrypt", lambda _token, _key: encrypted
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
ServiceTeleportError, match="Failed to create Le Chat thread"
|
||||
):
|
||||
await nuage.create_le_chat_thread("exec-123", "test message")
|
||||
|
||||
|
||||
class TestNuageClientHeaders:
|
||||
def test_headers_include_auth(self) -> None:
|
||||
nuage = NuageClient("https://api.example.com", "test-api-key", "workflow-id")
|
||||
headers = nuage._headers()
|
||||
assert headers["Authorization"] == "Bearer test-api-key"
|
||||
assert headers["Content-Type"] == "application/json"
|
||||
418
tests/core/test_teleport_service.py
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import importlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
import zstandard
|
||||
|
||||
from vibe.core.teleport.errors import (
|
||||
ServiceTeleportError,
|
||||
ServiceTeleportNotSupportedError,
|
||||
)
|
||||
from vibe.core.teleport.git import GitRepoInfo
|
||||
from vibe.core.teleport.nuage import TeleportSession
|
||||
from vibe.core.teleport.teleport import _NUAGE_EXECUTION_URL_TEMPLATE, TeleportService
|
||||
from vibe.core.teleport.types import (
|
||||
TeleportAuthCompleteEvent,
|
||||
TeleportAuthRequiredEvent,
|
||||
TeleportCheckingGitEvent,
|
||||
TeleportCompleteEvent,
|
||||
TeleportPushingEvent,
|
||||
TeleportPushRequiredEvent,
|
||||
TeleportPushResponseEvent,
|
||||
TeleportSendingGithubTokenEvent,
|
||||
TeleportStartingWorkflowEvent,
|
||||
)
|
||||
|
||||
|
||||
def _reimport_agent_loop() -> Any:
|
||||
to_clear = ("vibe.core.agent_loop", "git", "vibe.core.teleport")
|
||||
for k in [k for k in sys.modules if any(k.startswith(m) for m in to_clear)]:
|
||||
del sys.modules[k]
|
||||
return importlib.import_module("vibe.core.agent_loop")
|
||||
|
||||
|
||||
class TestTeleportServiceCompressDiff:
|
||||
@pytest.fixture
|
||||
def service(self, tmp_path: Path) -> TeleportService:
|
||||
mock_session_logger = MagicMock()
|
||||
return TeleportService(
|
||||
session_logger=mock_session_logger,
|
||||
nuage_base_url="https://api.example.com",
|
||||
nuage_workflow_id="workflow-id",
|
||||
nuage_api_key="api-key",
|
||||
workdir=tmp_path,
|
||||
)
|
||||
|
||||
def test_returns_none_for_empty_diff(self, service: TeleportService) -> None:
|
||||
assert service._compress_diff("") is None
|
||||
|
||||
def test_compresses_and_encodes_diff(self, service: TeleportService) -> None:
|
||||
diff = "diff --git a/file.txt b/file.txt\n+new line"
|
||||
result = service._compress_diff(diff)
|
||||
|
||||
assert result is not None
|
||||
decoded = base64.b64decode(result)
|
||||
decompressed = zstandard.ZstdDecompressor().decompress(decoded)
|
||||
assert decompressed.decode("utf-8") == diff
|
||||
|
||||
def test_raises_when_diff_too_large(self, service: TeleportService) -> None:
|
||||
large_diff = "x" * 2_000_000
|
||||
with pytest.raises(ServiceTeleportError, match="Diff too large"):
|
||||
service._compress_diff(large_diff, max_size=100)
|
||||
|
||||
|
||||
class TestTeleportServiceBuildSandbox:
|
||||
@pytest.fixture
|
||||
def service(self, tmp_path: Path) -> TeleportService:
|
||||
mock_session_logger = MagicMock()
|
||||
return TeleportService(
|
||||
session_logger=mock_session_logger,
|
||||
nuage_base_url="https://api.example.com",
|
||||
nuage_workflow_id="workflow-id",
|
||||
nuage_api_key="api-key",
|
||||
workdir=tmp_path,
|
||||
)
|
||||
|
||||
def test_builds_sandbox_from_git_info(self, service: TeleportService) -> None:
|
||||
git_info = GitRepoInfo(
|
||||
remote_url="https://github.com/owner/repo.git",
|
||||
owner="owner",
|
||||
repo="repo",
|
||||
branch="main",
|
||||
commit="abc123",
|
||||
diff="",
|
||||
)
|
||||
sandbox = service._build_sandbox(git_info)
|
||||
|
||||
assert sandbox.type == "new"
|
||||
assert sandbox.config.git_repo is not None
|
||||
assert sandbox.config.git_repo.url == "https://github.com/owner/repo.git"
|
||||
assert sandbox.config.git_repo.branch == "main"
|
||||
assert sandbox.config.git_repo.commit == "abc123"
|
||||
assert sandbox.teleported_diffs is None
|
||||
|
||||
def test_includes_compressed_diff(self, service: TeleportService) -> None:
|
||||
git_info = GitRepoInfo(
|
||||
remote_url="https://github.com/owner/repo.git",
|
||||
owner="owner",
|
||||
repo="repo",
|
||||
branch="main",
|
||||
commit="abc123",
|
||||
diff="diff content",
|
||||
)
|
||||
sandbox = service._build_sandbox(git_info)
|
||||
|
||||
assert sandbox.teleported_diffs is not None
|
||||
|
||||
|
||||
class TestTeleportServiceValidateConfig:
|
||||
def test_raises_when_no_api_key(self, tmp_path: Path) -> None:
|
||||
mock_session_logger = MagicMock()
|
||||
service = TeleportService(
|
||||
session_logger=mock_session_logger,
|
||||
nuage_base_url="https://api.example.com",
|
||||
nuage_workflow_id="workflow-id",
|
||||
nuage_api_key="",
|
||||
workdir=tmp_path,
|
||||
)
|
||||
with pytest.raises(
|
||||
ServiceTeleportError, match="STAGING_MISTRAL_API_KEY not set"
|
||||
):
|
||||
service._validate_config()
|
||||
|
||||
def test_passes_when_api_key_set(self, tmp_path: Path) -> None:
|
||||
mock_session_logger = MagicMock()
|
||||
service = TeleportService(
|
||||
session_logger=mock_session_logger,
|
||||
nuage_base_url="https://api.example.com",
|
||||
nuage_workflow_id="workflow-id",
|
||||
nuage_api_key="valid-key",
|
||||
workdir=tmp_path,
|
||||
)
|
||||
service._validate_config()
|
||||
|
||||
|
||||
class TestTeleportServiceCheckSupported:
|
||||
@pytest.fixture
|
||||
def service(self, tmp_path: Path) -> TeleportService:
|
||||
mock_session_logger = MagicMock()
|
||||
return TeleportService(
|
||||
session_logger=mock_session_logger,
|
||||
nuage_base_url="https://api.example.com",
|
||||
nuage_workflow_id="workflow-id",
|
||||
nuage_api_key="api-key",
|
||||
workdir=tmp_path,
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_supported_calls_git_info(
|
||||
self, service: TeleportService
|
||||
) -> None:
|
||||
service._git.get_info = AsyncMock(
|
||||
return_value=GitRepoInfo(
|
||||
remote_url="https://github.com/owner/repo.git",
|
||||
owner="owner",
|
||||
repo="repo",
|
||||
branch="main",
|
||||
commit="abc123",
|
||||
diff="",
|
||||
)
|
||||
)
|
||||
await service.check_supported()
|
||||
service._git.get_info.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_check_supported_raises_when_not_supported(
|
||||
self, service: TeleportService
|
||||
) -> None:
|
||||
service._git.get_info = AsyncMock(
|
||||
side_effect=ServiceTeleportNotSupportedError("Not a git repository")
|
||||
)
|
||||
with pytest.raises(ServiceTeleportNotSupportedError):
|
||||
await service.check_supported()
|
||||
|
||||
|
||||
class TestTeleportServiceIsSupported:
|
||||
@pytest.fixture
|
||||
def service(self, tmp_path: Path) -> TeleportService:
|
||||
mock_session_logger = MagicMock()
|
||||
return TeleportService(
|
||||
session_logger=mock_session_logger,
|
||||
nuage_base_url="https://api.example.com",
|
||||
nuage_workflow_id="workflow-id",
|
||||
nuage_api_key="api-key",
|
||||
workdir=tmp_path,
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_supported_returns_true(self, service: TeleportService) -> None:
|
||||
service._git.is_supported = AsyncMock(return_value=True)
|
||||
assert await service.is_supported() is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_supported_returns_false(self, service: TeleportService) -> None:
|
||||
service._git.is_supported = AsyncMock(return_value=False)
|
||||
assert await service.is_supported() is False
|
||||
|
||||
|
||||
class TestTeleportServiceExecute:
|
||||
@pytest.fixture
|
||||
def service(self, tmp_path: Path) -> TeleportService:
|
||||
mock_session_logger = MagicMock()
|
||||
service = TeleportService(
|
||||
session_logger=mock_session_logger,
|
||||
nuage_base_url="https://api.example.com",
|
||||
nuage_workflow_id="workflow-id",
|
||||
nuage_api_key="api-key",
|
||||
workdir=tmp_path,
|
||||
)
|
||||
return service
|
||||
|
||||
@pytest.fixture
|
||||
def git_info(self) -> GitRepoInfo:
|
||||
return GitRepoInfo(
|
||||
remote_url="https://github.com/owner/repo.git",
|
||||
owner="owner",
|
||||
repo="repo",
|
||||
branch="main",
|
||||
commit="abc123",
|
||||
diff="",
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_happy_path_commit_pushed_with_token(
|
||||
self, service: TeleportService, git_info: GitRepoInfo
|
||||
) -> None:
|
||||
service._git.get_info = AsyncMock(return_value=git_info)
|
||||
service._git.is_commit_pushed = AsyncMock(return_value=True)
|
||||
|
||||
mock_github_auth = MagicMock()
|
||||
mock_github_auth.get_valid_token = AsyncMock(return_value="ghp_existing_token")
|
||||
service._github_auth = mock_github_auth
|
||||
|
||||
mock_nuage = MagicMock()
|
||||
mock_nuage.start_workflow = AsyncMock(return_value="exec-123")
|
||||
mock_nuage.send_github_token = AsyncMock()
|
||||
service._nuage = mock_nuage
|
||||
|
||||
session = TeleportSession()
|
||||
events = []
|
||||
gen = service.execute("test prompt", session)
|
||||
async for event in gen:
|
||||
events.append(event)
|
||||
|
||||
assert isinstance(events[0], TeleportCheckingGitEvent)
|
||||
assert isinstance(events[1], TeleportStartingWorkflowEvent)
|
||||
assert isinstance(events[2], TeleportSendingGithubTokenEvent)
|
||||
assert isinstance(events[3], TeleportCompleteEvent)
|
||||
expected_url = _NUAGE_EXECUTION_URL_TEMPLATE.format(
|
||||
workflow_id="workflow-id", execution_id="exec-123"
|
||||
)
|
||||
assert events[3].url == expected_url
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_requires_push_and_user_approves(
|
||||
self, service: TeleportService, git_info: GitRepoInfo
|
||||
) -> None:
|
||||
service._git.get_info = AsyncMock(return_value=git_info)
|
||||
service._git.is_commit_pushed = AsyncMock(return_value=False)
|
||||
service._git.get_unpushed_commit_count = AsyncMock(return_value=3)
|
||||
service._git.push_current_branch = AsyncMock(return_value=True)
|
||||
|
||||
mock_github_auth = MagicMock()
|
||||
mock_github_auth.get_valid_token = AsyncMock(return_value="ghp_token")
|
||||
service._github_auth = mock_github_auth
|
||||
|
||||
mock_nuage = MagicMock()
|
||||
mock_nuage.start_workflow = AsyncMock(return_value="exec-123")
|
||||
mock_nuage.send_github_token = AsyncMock()
|
||||
service._nuage = mock_nuage
|
||||
|
||||
session = TeleportSession()
|
||||
events = []
|
||||
gen = service.execute("test prompt", session)
|
||||
|
||||
event = await gen.asend(None)
|
||||
events.append(event)
|
||||
assert isinstance(event, TeleportCheckingGitEvent)
|
||||
|
||||
event = await gen.asend(None)
|
||||
events.append(event)
|
||||
assert isinstance(event, TeleportPushRequiredEvent)
|
||||
assert event.unpushed_count == 3
|
||||
|
||||
event = await gen.asend(TeleportPushResponseEvent(approved=True))
|
||||
events.append(event)
|
||||
assert isinstance(event, TeleportPushingEvent)
|
||||
|
||||
async for event in gen:
|
||||
events.append(event)
|
||||
|
||||
assert isinstance(events[-1], TeleportCompleteEvent)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_requires_push_and_user_declines(
|
||||
self, service: TeleportService, git_info: GitRepoInfo
|
||||
) -> None:
|
||||
service._git.get_info = AsyncMock(return_value=git_info)
|
||||
service._git.is_commit_pushed = AsyncMock(return_value=False)
|
||||
service._git.get_unpushed_commit_count = AsyncMock(return_value=1)
|
||||
|
||||
session = TeleportSession()
|
||||
gen = service.execute("test prompt", session)
|
||||
|
||||
await gen.asend(None)
|
||||
await gen.asend(None)
|
||||
|
||||
with pytest.raises(ServiceTeleportError, match="Teleport cancelled"):
|
||||
await gen.asend(TeleportPushResponseEvent(approved=False))
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_requires_auth_flow(
|
||||
self, service: TeleportService, git_info: GitRepoInfo
|
||||
) -> None:
|
||||
service._git.get_info = AsyncMock(return_value=git_info)
|
||||
service._git.is_commit_pushed = AsyncMock(return_value=True)
|
||||
|
||||
mock_handle = MagicMock()
|
||||
mock_handle.info.user_code = "ABC-123"
|
||||
mock_handle.info.verification_uri = "https://github.com/login/device"
|
||||
|
||||
mock_github_auth = MagicMock()
|
||||
mock_github_auth.get_valid_token = AsyncMock(return_value=None)
|
||||
mock_github_auth.start_device_flow = AsyncMock(return_value=mock_handle)
|
||||
mock_github_auth.wait_for_token = AsyncMock(return_value="ghp_new_token")
|
||||
service._github_auth = mock_github_auth
|
||||
|
||||
mock_nuage = MagicMock()
|
||||
mock_nuage.start_workflow = AsyncMock(return_value="exec-123")
|
||||
mock_nuage.send_github_token = AsyncMock()
|
||||
service._nuage = mock_nuage
|
||||
|
||||
session = TeleportSession()
|
||||
events = []
|
||||
gen = service.execute("test prompt", session)
|
||||
async for event in gen:
|
||||
events.append(event)
|
||||
|
||||
assert isinstance(events[0], TeleportCheckingGitEvent)
|
||||
assert isinstance(events[1], TeleportAuthRequiredEvent)
|
||||
assert events[1].user_code == "ABC-123"
|
||||
assert isinstance(events[2], TeleportAuthCompleteEvent)
|
||||
assert isinstance(events[3], TeleportStartingWorkflowEvent)
|
||||
assert isinstance(events[-1], TeleportCompleteEvent)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_uses_default_prompt_when_none(
|
||||
self, service: TeleportService, git_info: GitRepoInfo
|
||||
) -> None:
|
||||
service._git.get_info = AsyncMock(return_value=git_info)
|
||||
service._git.is_commit_pushed = AsyncMock(return_value=True)
|
||||
|
||||
mock_github_auth = MagicMock()
|
||||
mock_github_auth.get_valid_token = AsyncMock(return_value="ghp_token")
|
||||
service._github_auth = mock_github_auth
|
||||
|
||||
mock_nuage = MagicMock()
|
||||
mock_nuage.start_workflow = AsyncMock(return_value="exec-123")
|
||||
mock_nuage.send_github_token = AsyncMock()
|
||||
service._nuage = mock_nuage
|
||||
|
||||
session = TeleportSession()
|
||||
gen = service.execute(None, session)
|
||||
async for _ in gen:
|
||||
pass
|
||||
|
||||
call_args = mock_nuage.start_workflow.call_args
|
||||
assert "continue where you left off" in call_args[0][0].prompt
|
||||
|
||||
|
||||
class TestTeleportServiceContextManager:
|
||||
@pytest.mark.asyncio
|
||||
async def test_creates_client_on_enter(self, tmp_path: Path) -> None:
|
||||
mock_session_logger = MagicMock()
|
||||
service = TeleportService(
|
||||
session_logger=mock_session_logger,
|
||||
nuage_base_url="https://api.example.com",
|
||||
nuage_workflow_id="workflow-id",
|
||||
nuage_api_key="api-key",
|
||||
workdir=tmp_path,
|
||||
)
|
||||
assert service._client is None
|
||||
async with service:
|
||||
assert service._client is not None
|
||||
assert service._github_auth is not None
|
||||
assert service._nuage is not None
|
||||
assert service._client is None
|
||||
|
||||
|
||||
class TestTeleportAvailability:
|
||||
def test_teleport_available_is_false_when_git_not_installed(self) -> None:
|
||||
with patch.dict(os.environ, {"GIT_PYTHON_GIT_EXECUTABLE": "/nonexistent/git"}):
|
||||
agent_loop = _reimport_agent_loop()
|
||||
assert agent_loop._TELEPORT_AVAILABLE is False
|
||||
|
||||
def test_teleport_service_raises_error_when_git_not_available(self) -> None:
|
||||
with patch.dict(os.environ, {"GIT_PYTHON_GIT_EXECUTABLE": "/nonexistent/git"}):
|
||||
agent_loop = _reimport_agent_loop()
|
||||
with pytest.raises(agent_loop.TeleportError, match="git to be installed"):
|
||||
agent_loop.AgentLoop.teleport_service.fget(
|
||||
MagicMock(_teleport_service=None)
|
||||
)
|
||||
|
||||
def test_teleport_available_is_true_when_git_installed(
|
||||
self, tmp_path: Path
|
||||
) -> None:
|
||||
fake_git = tmp_path / "git"
|
||||
fake_git.write_text("#!/bin/sh\necho 'git version 2.0.0'")
|
||||
fake_git.chmod(0o755)
|
||||
with patch.dict(os.environ, {"GIT_PYTHON_GIT_EXECUTABLE": str(fake_git)}):
|
||||
agent_loop = _reimport_agent_loop()
|
||||
assert agent_loop._TELEPORT_AVAILABLE is True
|
||||
|
|
@ -51,7 +51,6 @@ def get_mocking_env(mock_chunks: list[LLMChunk] | None = None) -> dict[str, str]
|
|||
|
||||
|
||||
async def collect_result[T](gen: AsyncGenerator[ToolStreamEvent | T, None]) -> T:
|
||||
"""Collect the final result from an AsyncGenerator, filtering out stream events."""
|
||||
result = None
|
||||
async for item in gen:
|
||||
if not isinstance(item, ToolStreamEvent):
|
||||
|
|
|
|||
|
|
@ -1,19 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
import tomllib
|
||||
|
||||
import pytest
|
||||
from textual.events import Resize
|
||||
from textual.geometry import Size
|
||||
from textual.pilot import Pilot
|
||||
from textual.widgets import Input
|
||||
|
||||
from vibe.core.paths.global_paths import GLOBAL_CONFIG_FILE, GLOBAL_ENV_FILE
|
||||
from vibe.core.paths.global_paths import GLOBAL_ENV_FILE
|
||||
from vibe.setup.onboarding import OnboardingApp
|
||||
from vibe.setup.onboarding.screens.api_key import ApiKeyScreen
|
||||
from vibe.setup.onboarding.screens.theme_selection import ThemeSelectionScreen
|
||||
|
||||
|
||||
async def _wait_for(
|
||||
|
|
@ -36,7 +31,7 @@ async def pass_welcome_screen(pilot: Pilot) -> None:
|
|||
lambda: not welcome_screen.query_one("#enter-hint").has_class("hidden"), pilot
|
||||
)
|
||||
await pilot.press("enter")
|
||||
await _wait_for(lambda: isinstance(pilot.app.screen, ThemeSelectionScreen), pilot)
|
||||
await _wait_for(lambda: isinstance(pilot.app.screen, ApiKeyScreen), pilot)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -47,8 +42,6 @@ async def test_ui_gets_through_the_onboarding_successfully() -> None:
|
|||
async with app.run_test() as pilot:
|
||||
await pass_welcome_screen(pilot)
|
||||
|
||||
await pilot.press("enter")
|
||||
await _wait_for(lambda: isinstance(app.screen, ApiKeyScreen), pilot)
|
||||
api_screen = app.screen
|
||||
input_widget = api_screen.query_one("#key", Input)
|
||||
await pilot.press(*api_key_value)
|
||||
|
|
@ -63,40 +56,3 @@ async def test_ui_gets_through_the_onboarding_successfully() -> None:
|
|||
env_contents = GLOBAL_ENV_FILE.path.read_text(encoding="utf-8")
|
||||
assert "MISTRAL_API_KEY" in env_contents
|
||||
assert api_key_value in env_contents
|
||||
|
||||
assert GLOBAL_CONFIG_FILE.path.is_file()
|
||||
config_contents = GLOBAL_CONFIG_FILE.path.read_text(encoding="utf-8")
|
||||
config_dict = tomllib.loads(config_contents)
|
||||
assert config_dict.get("textual_theme") == app.theme
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ui_can_pick_a_theme_and_saves_selection(config_dir: Path) -> None:
|
||||
app = OnboardingApp()
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pass_welcome_screen(pilot)
|
||||
|
||||
theme_screen = app.screen
|
||||
assert isinstance(theme_screen, ThemeSelectionScreen)
|
||||
app.post_message(
|
||||
Resize(Size(40, 10), Size(40, 10))
|
||||
) # trigger the resize event handler
|
||||
preview = theme_screen.query_one("#preview")
|
||||
assert preview.styles.max_height is not None
|
||||
target_theme = "gruvbox"
|
||||
# Use the screen's available themes which accounts for terminal theme availability
|
||||
available_themes = theme_screen._available_themes
|
||||
assert target_theme in available_themes
|
||||
start_index = theme_screen._theme_index
|
||||
target_index = available_themes.index(target_theme)
|
||||
steps_down = (target_index - start_index) % len(available_themes)
|
||||
await pilot.press(*["down"] * steps_down)
|
||||
assert app.theme == target_theme
|
||||
await pilot.press("enter")
|
||||
await _wait_for(lambda: isinstance(app.screen, ApiKeyScreen), pilot)
|
||||
|
||||
assert GLOBAL_CONFIG_FILE.path.is_file()
|
||||
config_contents = GLOBAL_CONFIG_FILE.path.read_text(encoding="utf-8")
|
||||
config_dict = tomllib.loads(config_contents)
|
||||
assert config_dict.get("textual_theme") == target_theme
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from unittest.mock import MagicMock, patch
|
|||
|
||||
import pytest
|
||||
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from vibe.core.agents.models import AgentProfile, AgentSafety
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
from vibe.core.session.session_logger import SessionLogger
|
||||
|
|
@ -62,7 +63,7 @@ def mock_tool_manager() -> ToolManager:
|
|||
@pytest.fixture
|
||||
def mock_vibe_config() -> VibeConfig:
|
||||
"""Create a mock vibe config for testing."""
|
||||
return VibeConfig(active_model="test-model", models=[], providers=[])
|
||||
return build_test_vibe_config(active_model="test-model", models=[], providers=[])
|
||||
|
||||
|
||||
class TestSessionLoggerInitialization:
|
||||
|
|
@ -171,7 +172,9 @@ class TestSessionLoggerSaveInteraction:
|
|||
result = await logger.save_interaction(
|
||||
messages=[],
|
||||
stats=AgentStats(),
|
||||
base_config=VibeConfig(active_model="test", models=[], providers=[]),
|
||||
base_config=build_test_vibe_config(
|
||||
active_model="test", models=[], providers=[]
|
||||
),
|
||||
tool_manager=MagicMock(),
|
||||
agent_profile=AgentProfile(
|
||||
name="test",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ from pathlib import Path
|
|||
import pytest
|
||||
import yaml
|
||||
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from vibe.core.config import VibeConfig
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -18,8 +19,7 @@ def skills_dir(tmp_path: Path) -> Path:
|
|||
|
||||
@pytest.fixture
|
||||
def skill_config(skills_dir: Path) -> VibeConfig:
|
||||
return VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
return build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
|
|||
|
|
@ -4,17 +4,16 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from tests.conftest import build_test_vibe_config
|
||||
from tests.skills.conftest import create_skill
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
from vibe.core.config import VibeConfig
|
||||
from vibe.core.skills.manager import SkillManager
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config() -> VibeConfig:
|
||||
return VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
return build_test_vibe_config(
|
||||
system_prompt_id="tests", include_project_context=False
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -32,8 +31,7 @@ class TestSkillManagerDiscovery:
|
|||
def test_discovers_skill_from_skill_paths(self, skills_dir: Path) -> None:
|
||||
create_skill(skills_dir, "test-skill", "A test skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -48,8 +46,7 @@ class TestSkillManagerDiscovery:
|
|||
create_skill(skills_dir, "skill-two", "Second skill")
|
||||
create_skill(skills_dir, "skill-three", "Third skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -70,8 +67,7 @@ class TestSkillManagerDiscovery:
|
|||
# Create a valid skill
|
||||
create_skill(skills_dir, "valid-skill", "A valid skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -90,8 +86,7 @@ class TestSkillManagerDiscovery:
|
|||
# Create a valid skill
|
||||
create_skill(skills_dir, "valid-skill", "A valid skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -115,8 +110,7 @@ class TestSkillManagerParsing:
|
|||
allowed_tools="bash read_file",
|
||||
)
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -135,8 +129,7 @@ class TestSkillManagerParsing:
|
|||
def test_sets_correct_skill_path(self, skills_dir: Path) -> None:
|
||||
create_skill(skills_dir, "test-skill", "A test skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -157,8 +150,7 @@ class TestSkillManagerParsing:
|
|||
# Create a valid skill
|
||||
create_skill(skills_dir, "valid-skill", "A valid skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -179,8 +171,7 @@ class TestSkillManagerParsing:
|
|||
# Create a valid skill
|
||||
create_skill(skills_dir, "valid-skill", "A valid skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -203,8 +194,7 @@ class TestSkillManagerSearchPaths:
|
|||
skills_dir_2.mkdir()
|
||||
create_skill(skills_dir_2, "skill-from-dir2", "Skill from directory 2")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir_1, skills_dir_2],
|
||||
|
|
@ -226,8 +216,7 @@ class TestSkillManagerSearchPaths:
|
|||
skills_dir_2.mkdir()
|
||||
create_skill(skills_dir_2, "duplicate-skill", "Second version")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir_1, skills_dir_2],
|
||||
|
|
@ -243,8 +232,7 @@ class TestSkillManagerSearchPaths:
|
|||
skills_dir.mkdir()
|
||||
create_skill(skills_dir, "valid-skill", "A valid skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir, tmp_path / "nonexistent"],
|
||||
|
|
@ -259,8 +247,7 @@ class TestSkillManagerGetSkill:
|
|||
def test_returns_skill_by_name(self, skills_dir: Path) -> None:
|
||||
create_skill(skills_dir, "test-skill", "A test skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -281,8 +268,7 @@ class TestSkillManagerFiltering:
|
|||
create_skill(skills_dir, "skill-b", "Skill B")
|
||||
create_skill(skills_dir, "skill-c", "Skill C")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -301,8 +287,7 @@ class TestSkillManagerFiltering:
|
|||
create_skill(skills_dir, "skill-b", "Skill B")
|
||||
create_skill(skills_dir, "skill-c", "Skill C")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -322,8 +307,7 @@ class TestSkillManagerFiltering:
|
|||
create_skill(skills_dir, "skill-a", "Skill A")
|
||||
create_skill(skills_dir, "skill-b", "Skill B")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -341,8 +325,7 @@ class TestSkillManagerFiltering:
|
|||
create_skill(skills_dir, "search-docs", "Search docs")
|
||||
create_skill(skills_dir, "other-skill", "Other skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -361,8 +344,7 @@ class TestSkillManagerFiltering:
|
|||
create_skill(skills_dir, "skill-v2", "Skill v2")
|
||||
create_skill(skills_dir, "other-skill", "Other skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -380,8 +362,7 @@ class TestSkillManagerFiltering:
|
|||
create_skill(skills_dir, "enabled-skill", "Enabled")
|
||||
create_skill(skills_dir, "disabled-skill", "Disabled")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -397,8 +378,7 @@ class TestSkillUserInvocable:
|
|||
def test_user_invocable_defaults_to_true(self, skills_dir: Path) -> None:
|
||||
create_skill(skills_dir, "default-skill", "A default skill")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -412,8 +392,7 @@ class TestSkillUserInvocable:
|
|||
def test_user_invocable_can_be_set_to_false(self, skills_dir: Path) -> None:
|
||||
create_skill(skills_dir, "hidden-skill", "A hidden skill", user_invocable=False)
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -431,8 +410,7 @@ class TestSkillUserInvocable:
|
|||
skills_dir, "explicit-skill", "An explicit skill", user_invocable=True
|
||||
)
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
@ -448,8 +426,7 @@ class TestSkillUserInvocable:
|
|||
create_skill(skills_dir, "hidden-skill", "Hidden", user_invocable=False)
|
||||
create_skill(skills_dir, "default-skill", "Default")
|
||||
|
||||
config = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
config = build_test_vibe_config(
|
||||
system_prompt_id="tests",
|
||||
include_project_context=False,
|
||||
skill_paths=[skills_dir],
|
||||
|
|
|
|||
|
|
@ -33,13 +33,9 @@
|
|||
}
|
||||
|
||||
.terminal-r1 { fill: #c5c8c6 }
|
||||
.terminal-r2 { fill: #3c3836 }
|
||||
.terminal-r3 { fill: #fbf1c7 }
|
||||
.terminal-r4 { fill: #a6a087 }
|
||||
.terminal-r5 { fill: #a9a9a9 }
|
||||
.terminal-r6 { fill: #85a598;font-weight: bold }
|
||||
.terminal-r7 { fill: #7e7e7e }
|
||||
.terminal-r8 { fill: #85a598 }
|
||||
.terminal-r2 { fill: #ff8205;font-weight: bold }
|
||||
.terminal-r3 { fill: #68a0b3 }
|
||||
.terminal-r4 { fill: #9a9b99 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -113,28 +109,28 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#282828" x="0" y="1.5" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="25.9" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="12.2" y="50.3" width="1439.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="1451.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="12.2" y="74.7" width="1439.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="1451.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="12.2" y="99.1" width="707.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="719.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="732" y="99.1" width="719.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="1451.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="12.2" y="123.5" width="1439.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="1451.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="12.2" y="147.9" width="1439.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="1451.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="172.3" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="196.7" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="221.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="24.4" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="36.6" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="48.8" y="221.1" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="500.2" y="221.1" width="963.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="245.5" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="269.9" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="294.3" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="318.7" width="0" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="318.7" width="1024.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="1024.8" y="318.7" width="439.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="343.1" width="0" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="343.1" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="367.5" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="391.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="12.2" y="391.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="24.4" y="391.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="36.6" y="391.9" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="219.6" y="391.9" width="1244.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="416.3" width="1464" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="440.7" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="158.6" y="440.7" width="0" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="158.6" y="440.7" width="1098" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="1256.6" y="440.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#282828" x="0" y="465.1" width="1464" height="24.65" shape-rendering="crispEdges"/>
|
||||
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r2" x="0" y="44.4" textLength="1464" clip-path="url(#terminal-line-1)">╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="1451.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="1451.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="1451.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="1451.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="1451.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="0" y="190.8" textLength="1464" clip-path="url(#terminal-line-7)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-line-0)">  ⡠⣒⠄  ⡔⢄⠔⡄</text><text class="terminal-r2" x="170.8" y="20" textLength="146.4" clip-path="url(#terminal-line-0)">Mistral Vibe</text><text class="terminal-r1" x="317.2" y="20" textLength="122" clip-path="url(#terminal-line-0)"> v2.1.0 · </text><text class="terminal-r3" x="439.2" y="20" textLength="183" clip-path="url(#terminal-line-0)">devstral-latest</text><text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="134.2" clip-path="url(#terminal-line-1)"> ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣</text><text class="terminal-r1" x="170.8" y="44.4" textLength="414.8" clip-path="url(#terminal-line-1)">1 model · 0 MCP servers · 0 skills</text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="134.2" clip-path="url(#terminal-line-2)">  ⠉⠒⠣⠤⠵⠤⠬⠮⠆</text><text class="terminal-r1" x="170.8" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">Type </text><text class="terminal-r3" x="231.8" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">/help</text><text class="terminal-r1" x="292.8" y="68.8" textLength="256.2" clip-path="url(#terminal-line-2)"> for more information</text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r4" x="24.4" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">⎣</text><text class="terminal-r3" x="48.8" y="239.6" textLength="451.4" clip-path="url(#terminal-line-9)">3 answers received (ctrl+o to expand)</text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r1" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r5" x="1024.8" y="337.2" textLength="439.2" clip-path="url(#terminal-line-13)">⏵ default agent (shift+tab to cycle)</text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
</text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
|
||||
</text><text class="terminal-r4" x="0" y="386" textLength="1464" clip-path="url(#terminal-line-15)">────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
|
||||
</text><text class="terminal-r6" x="12.2" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">></text><text class="terminal-r7" x="36.6" y="410.4" textLength="183" clip-path="url(#terminal-line-16)">Ask anything...</text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
|
||||
</text><text class="terminal-r4" x="0" y="434.8" textLength="1464" clip-path="url(#terminal-line-17)">────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
|
||||
</text><text class="terminal-r8" x="0" y="459.2" textLength="158.6" clip-path="url(#terminal-line-18)">/test/workdir</text><text class="terminal-r3" x="1256.6" y="459.2" textLength="207.4" clip-path="url(#terminal-line-18)">0% of 200k tokens</text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
|
||||
</text>
|
||||
</text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
</text><text class="terminal-r4" x="0" y="361.6" textLength="1464" clip-path="url(#terminal-line-14)">┌──────────────────────────────────────────────────────────────────────────────────────────────────────────── default ─┐</text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
|
||||
</text><text class="terminal-r4" x="0" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">│</text><text class="terminal-r2" x="24.4" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">></text><text class="terminal-r4" x="1451.8" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">│</text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
|
||||
</text><text class="terminal-r4" x="0" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">│</text><text class="terminal-r4" x="1451.8" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">│</text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
|
||||
</text><text class="terminal-r4" x="0" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">│</text><text class="terminal-r4" x="1451.8" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">│</text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
|
||||
</text><text class="terminal-r4" x="0" y="459.2" textLength="1464" clip-path="url(#terminal-line-18)">└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
|
||||
</text><text class="terminal-r4" x="0" y="483.6" textLength="158.6" clip-path="url(#terminal-line-19)">/test/workdir</text><text class="terminal-r4" x="1256.6" y="483.6" textLength="207.4" clip-path="url(#terminal-line-19)">0% of 200k tokens</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
|
@ -32,11 +32,11 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4 }
|
||||
.terminal-r4 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r5 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff }
|
||||
.terminal-r4 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r5 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -110,19 +110,19 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="256.2" y="25.9" width="707.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="219.6" y="74.7" width="744.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="123.5" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="147.9" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="256.2" y="25.9" width="707.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="219.6" y="74.7" width="744.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="123.5" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="147.9" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="172.3" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="231.8" clip-path="url(#terminal-line-1)"> DB ✓   [Framework]</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="195.2" clip-path="url(#terminal-line-3)">Which framework?</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="146.4" clip-path="url(#terminal-line-5)">› 1. FastAPI</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="134.2" clip-path="url(#terminal-line-6)">  2. Django</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="292.8" clip-path="url(#terminal-line-7)">  3. Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="61" clip-path="url(#terminal-line-7)">  3. </text><text class="terminal-r1" x="85.4" y="190.8" textLength="231.8" clip-path="url(#terminal-line-7)">Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r1" x="24.4" y="239.6" textLength="622.2" clip-path="url(#terminal-line-9)">←→ questions  ↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,11 +32,11 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4 }
|
||||
.terminal-r4 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r5 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff }
|
||||
.terminal-r4 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r5 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -110,19 +110,19 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="256.2" y="25.9" width="707.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="219.6" y="74.7" width="744.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="123.5" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="147.9" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="256.2" y="25.9" width="707.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="219.6" y="74.7" width="744.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="123.5" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="147.9" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="172.3" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="231.8" clip-path="url(#terminal-line-1)"> DB ✓   [Framework]</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="195.2" clip-path="url(#terminal-line-3)">Which framework?</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="146.4" clip-path="url(#terminal-line-5)">› 1. FastAPI</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="134.2" clip-path="url(#terminal-line-6)">  2. Django</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="292.8" clip-path="url(#terminal-line-7)">  3. Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="61" clip-path="url(#terminal-line-7)">  3. </text><text class="terminal-r1" x="85.4" y="190.8" textLength="231.8" clip-path="url(#terminal-line-7)">Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r1" x="24.4" y="239.6" textLength="622.2" clip-path="url(#terminal-line-9)">←→ questions  ↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,11 +32,11 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4 }
|
||||
.terminal-r4 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r5 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff }
|
||||
.terminal-r4 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r5 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -110,19 +110,19 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="231.8" y="25.9" width="732" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="207.4" y="74.7" width="756.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="207.4" y="123.5" width="756.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="147.9" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="231.8" y="25.9" width="732" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="207.4" y="74.7" width="756.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="207.4" y="123.5" width="756.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="147.9" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="172.3" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="207.4" clip-path="url(#terminal-line-1)">[DB]   Framework </text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="183" clip-path="url(#terminal-line-3)">Which database?</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="183" clip-path="url(#terminal-line-5)">› 1. PostgreSQL</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="146.4" clip-path="url(#terminal-line-6)">  2. MongoDB</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="292.8" clip-path="url(#terminal-line-7)">  3. Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="61" clip-path="url(#terminal-line-7)">  3. </text><text class="terminal-r1" x="85.4" y="190.8" textLength="231.8" clip-path="url(#terminal-line-7)">Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r1" x="24.4" y="239.6" textLength="622.2" clip-path="url(#terminal-line-9)">←→ questions  ↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,11 +32,11 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4 }
|
||||
.terminal-r4 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r5 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff }
|
||||
.terminal-r4 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r5 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -110,19 +110,19 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="231.8" y="25.9" width="732" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="219.6" y="74.7" width="744.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="123.5" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="147.9" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="231.8" y="25.9" width="732" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="219.6" y="74.7" width="744.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="123.5" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="147.9" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="172.3" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="207.4" clip-path="url(#terminal-line-1)"> DB   [Framework]</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="195.2" clip-path="url(#terminal-line-3)">Which framework?</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="146.4" clip-path="url(#terminal-line-5)">› 1. FastAPI</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="134.2" clip-path="url(#terminal-line-6)">  2. Django</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="292.8" clip-path="url(#terminal-line-7)">  3. Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="61" clip-path="url(#terminal-line-7)">  3. </text><text class="terminal-r1" x="85.4" y="190.8" textLength="231.8" clip-path="url(#terminal-line-7)">Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r1" x="24.4" y="239.6" textLength="622.2" clip-path="url(#terminal-line-9)">←→ questions  ↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,11 +32,11 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4 }
|
||||
.terminal-r4 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r5 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff }
|
||||
.terminal-r4 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r5 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -110,19 +110,19 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="231.8" y="25.9" width="732" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="219.6" y="74.7" width="744.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="123.5" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="147.9" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="231.8" y="25.9" width="732" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="219.6" y="74.7" width="744.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="123.5" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="147.9" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="172.3" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="207.4" clip-path="url(#terminal-line-1)"> DB   [Framework]</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="195.2" clip-path="url(#terminal-line-3)">Which framework?</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="146.4" clip-path="url(#terminal-line-5)">› 1. FastAPI</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="134.2" clip-path="url(#terminal-line-6)">  2. Django</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="292.8" clip-path="url(#terminal-line-7)">  3. Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="61" clip-path="url(#terminal-line-7)">  3. </text><text class="terminal-r1" x="85.4" y="190.8" textLength="231.8" clip-path="url(#terminal-line-7)">Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r1" x="24.4" y="239.6" textLength="622.2" clip-path="url(#terminal-line-9)">←→ questions  ↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,11 +32,11 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4 }
|
||||
.terminal-r4 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r5 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff }
|
||||
.terminal-r4 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r5 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -110,19 +110,19 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="231.8" y="25.9" width="732" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="207.4" y="74.7" width="756.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="207.4" y="123.5" width="756.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="147.9" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="231.8" y="25.9" width="732" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="207.4" y="74.7" width="756.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="207.4" y="123.5" width="756.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="147.9" width="793" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="172.3" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="172.3" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="172.3" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="221.1" width="622.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="646.6" y="221.1" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="207.4" clip-path="url(#terminal-line-1)">[DB]   Framework </text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="183" clip-path="url(#terminal-line-3)">Which database?</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="183" clip-path="url(#terminal-line-5)">› 1. PostgreSQL</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="146.4" clip-path="url(#terminal-line-6)">  2. MongoDB</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="292.8" clip-path="url(#terminal-line-7)">  3. Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r5" x="24.4" y="190.8" textLength="61" clip-path="url(#terminal-line-7)">  3. </text><text class="terminal-r1" x="85.4" y="190.8" textLength="231.8" clip-path="url(#terminal-line-7)">Type your answer...</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r1" x="24.4" y="239.6" textLength="622.2" clip-path="url(#terminal-line-9)">←→ questions  ↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">│</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="0" y="264" textLength="976" clip-path="url(#terminal-line-10)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
|
@ -32,10 +32,10 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r4 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -109,18 +109,18 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="147.9" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="147.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="147.9" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="561.2" clip-path="url(#terminal-line-1)">Which database should we use for this project?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r3" x="24.4" y="93.2" textLength="451.4" clip-path="url(#terminal-line-3)">› 1. PostgreSQL - Relational database</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r4" x="24.4" y="117.6" textLength="390.4" clip-path="url(#terminal-line-4)">  2. MongoDB - Document database</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="341.6" clip-path="url(#terminal-line-5)">  3. Redis - In-memory store</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r4" x="24.4" y="166.4" textLength="292.8" clip-path="url(#terminal-line-6)">  4. Type your answer...</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r4" x="24.4" y="166.4" textLength="61" clip-path="url(#terminal-line-6)">  4. </text><text class="terminal-r1" x="85.4" y="166.4" textLength="231.8" clip-path="url(#terminal-line-6)">Type your answer...</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="24.4" y="215.2" textLength="451.4" clip-path="url(#terminal-line-8)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,10 +32,10 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r4 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -109,18 +109,18 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="147.9" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="147.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="147.9" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="561.2" clip-path="url(#terminal-line-1)">Which database should we use for this project?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="451.4" clip-path="url(#terminal-line-3)">  1. PostgreSQL - Relational database</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r3" x="24.4" y="117.6" textLength="390.4" clip-path="url(#terminal-line-4)">› 2. MongoDB - Document database</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="341.6" clip-path="url(#terminal-line-5)">  3. Redis - In-memory store</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r4" x="24.4" y="166.4" textLength="292.8" clip-path="url(#terminal-line-6)">  4. Type your answer...</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r4" x="24.4" y="166.4" textLength="61" clip-path="url(#terminal-line-6)">  4. </text><text class="terminal-r1" x="85.4" y="166.4" textLength="231.8" clip-path="url(#terminal-line-6)">Type your answer...</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="24.4" y="215.2" textLength="451.4" clip-path="url(#terminal-line-8)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,13 +32,14 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r4 { fill: #e0e0e0 }
|
||||
.terminal-r5 { fill: #e0e0e0;font-weight: bold }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
.terminal-r5 { fill: #d9d9d9;font-weight: bold }
|
||||
.terminal-r6 { fill: #121212 }
|
||||
.terminal-r7 { fill: #6c6c6c }
|
||||
.terminal-r8 { fill: #e0e0e0 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -114,7 +115,7 @@
|
|||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#e0e0e0" x="85.4" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="97.6" y="147.9" width="219.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="147.9" width="634.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="951.6" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="561.2" clip-path="url(#terminal-line-1)">Which database should we use for this project?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="451.4" clip-path="url(#terminal-line-3)">  1. PostgreSQL - Relational database</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
|
|
@ -123,7 +124,7 @@
|
|||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="61" clip-path="url(#terminal-line-6)">› 4. </text><text class="terminal-r6" x="85.4" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">T</text><text class="terminal-r7" x="97.6" y="166.4" textLength="219.6" clip-path="url(#terminal-line-6)">ype your answer...</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="24.4" y="215.2" textLength="451.4" clip-path="url(#terminal-line-8)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,10 +32,10 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r4 { fill: #e0e0e0 }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -109,18 +109,18 @@
|
|||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="292.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="147.9" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="147.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="147.9" width="646.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="561.2" clip-path="url(#terminal-line-1)">Which database should we use for this project?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="451.4" clip-path="url(#terminal-line-3)">  1. PostgreSQL - Relational database</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r4" x="24.4" y="117.6" textLength="390.4" clip-path="url(#terminal-line-4)">  2. MongoDB - Document database</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r3" x="24.4" y="142" textLength="341.6" clip-path="url(#terminal-line-5)">› 3. Redis - In-memory store</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r4" x="24.4" y="166.4" textLength="292.8" clip-path="url(#terminal-line-6)">  4. Type your answer...</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r4" x="24.4" y="166.4" textLength="61" clip-path="url(#terminal-line-6)">  4. </text><text class="terminal-r1" x="85.4" y="166.4" textLength="231.8" clip-path="url(#terminal-line-6)">Type your answer...</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="24.4" y="215.2" textLength="451.4" clip-path="url(#terminal-line-8)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,13 +32,14 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r4 { fill: #e0e0e0 }
|
||||
.terminal-r5 { fill: #e0e0e0;font-weight: bold }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
.terminal-r5 { fill: #d9d9d9;font-weight: bold }
|
||||
.terminal-r6 { fill: #121212 }
|
||||
.terminal-r7 { fill: #6c6c6c }
|
||||
.terminal-r8 { fill: #e0e0e0 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -114,7 +115,7 @@
|
|||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#e0e0e0" x="85.4" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="97.6" y="147.9" width="219.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="317.2" y="147.9" width="634.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="951.6" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="561.2" clip-path="url(#terminal-line-1)">Which database should we use for this project?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="451.4" clip-path="url(#terminal-line-3)">  1. PostgreSQL - Relational database</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
|
|
@ -123,7 +124,7 @@
|
|||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="61" clip-path="url(#terminal-line-6)">› 4. </text><text class="terminal-r6" x="85.4" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">T</text><text class="terminal-r7" x="97.6" y="166.4" textLength="219.6" clip-path="url(#terminal-line-6)">ype your answer...</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="24.4" y="215.2" textLength="451.4" clip-path="url(#terminal-line-8)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -32,12 +32,13 @@
|
|||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #8d8d8d }
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #0178d4;font-weight: bold }
|
||||
.terminal-r4 { fill: #e0e0e0 }
|
||||
.terminal-r5 { fill: #e0e0e0;font-weight: bold }
|
||||
.terminal-r6 { fill: #121212 }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
.terminal-r5 { fill: #d9d9d9;font-weight: bold }
|
||||
.terminal-r6 { fill: #e0e0e0 }
|
||||
.terminal-r7 { fill: #121212 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
|
|
@ -113,16 +114,16 @@
|
|||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="74.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="414.8" y="99.1" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="123.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="366" y="123.5" width="597.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="85.4" y="147.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#e0e0e0" x="158.6" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="170.8" y="147.9" width="780.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="951.6" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="172.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="196.7" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="196.7" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="196.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">╭──────────────────────────────────────────────────────────────────────────────╮</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="561.2" clip-path="url(#terminal-line-1)">Which database should we use for this project?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="451.4" clip-path="url(#terminal-line-3)">  1. PostgreSQL - Relational database</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r4" x="24.4" y="117.6" textLength="390.4" clip-path="url(#terminal-line-4)">  2. MongoDB - Document database</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r4" x="24.4" y="142" textLength="341.6" clip-path="url(#terminal-line-5)">  3. Redis - In-memory store</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="61" clip-path="url(#terminal-line-6)">› 4. </text><text class="terminal-r4" x="85.4" y="166.4" textLength="73.2" clip-path="url(#terminal-line-6)">SQLite</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r5" x="24.4" y="166.4" textLength="61" clip-path="url(#terminal-line-6)">› 4. </text><text class="terminal-r6" x="85.4" y="166.4" textLength="73.2" clip-path="url(#terminal-line-6)">SQLite</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r1" x="963.8" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">│</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r1" x="24.4" y="215.2" textLength="451.4" clip-path="url(#terminal-line-8)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">│</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">╰──────────────────────────────────────────────────────────────────────────────╯</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="0" y="239.6" textLength="976" clip-path="url(#terminal-line-9)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 13 KiB |
|
|
@ -0,0 +1,104 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 342.79999999999995" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="291.79999999999995" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-9">
|
||||
<rect x="0" y="221.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-10">
|
||||
<rect x="0" y="245.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="340.8" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportPushConfirmationMultipleCommitsApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="268.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="292.8" y="74.7" width="671" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="99.1" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="147.9" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="561.2" clip-path="url(#terminal-line-1)">You have 5 unpushed commits. Push to continue?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r4" x="24.4" y="93.2" textLength="268.4" clip-path="url(#terminal-line-3)">  1. Push and continue</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r3" x="24.4" y="117.6" textLength="134.2" clip-path="url(#terminal-line-4)">› 2. Cancel</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r1" x="24.4" y="166.4" textLength="451.4" clip-path="url(#terminal-line-6)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="976" clip-path="url(#terminal-line-7)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -0,0 +1,104 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 342.79999999999995" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="291.79999999999995" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-9">
|
||||
<rect x="0" y="221.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-10">
|
||||
<rect x="0" y="245.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="340.8" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportPushConfirmationMultipleCommitsApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="25.9" width="378.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="268.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="292.8" y="74.7" width="671" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="99.1" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="147.9" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="561.2" clip-path="url(#terminal-line-1)">You have 5 unpushed commits. Push to continue?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r3" x="24.4" y="93.2" textLength="268.4" clip-path="url(#terminal-line-3)">› 1. Push and continue</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r4" x="24.4" y="117.6" textLength="134.2" clip-path="url(#terminal-line-4)">  2. Cancel</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r1" x="24.4" y="166.4" textLength="451.4" clip-path="url(#terminal-line-6)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="976" clip-path="url(#terminal-line-7)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -0,0 +1,104 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 342.79999999999995" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #625e4c }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #9d65ff;font-weight: bold }
|
||||
.terminal-r4 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="291.79999999999995" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-9">
|
||||
<rect x="0" y="221.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-10">
|
||||
<rect x="0" y="245.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="340.8" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportPushConfirmationSingleCommitApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="573.4" y="25.9" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="50.3" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="74.7" width="268.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="292.8" y="74.7" width="671" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="99.1" width="134.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="158.6" y="99.1" width="805.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="123.5" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="147.9" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="147.9" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="963.8" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="976" clip-path="url(#terminal-line-0)">┌──────────────────────────────────────────────────────────────────────────────┐</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r3" x="24.4" y="44.4" textLength="549" clip-path="url(#terminal-line-1)">You have 1 unpushed commit. Push to continue?</text><text class="terminal-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">│</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">│</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r3" x="24.4" y="93.2" textLength="268.4" clip-path="url(#terminal-line-3)">› 1. Push and continue</text><text class="terminal-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">│</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r4" x="24.4" y="117.6" textLength="134.2" clip-path="url(#terminal-line-4)">  2. Cancel</text><text class="terminal-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">│</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r1" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">│</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r1" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r1" x="24.4" y="166.4" textLength="451.4" clip-path="url(#terminal-line-6)">↑↓ navigate  Enter select  Esc cancel</text><text class="terminal-r1" x="963.8" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">│</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r1" x="0" y="190.8" textLength="976" clip-path="url(#terminal-line-7)">└──────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
|
@ -0,0 +1,94 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 294.0" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #d9d9d9 }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="243.0" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="292" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportMessageAuthCompleteApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="1.5" width="256.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="280.6" y="1.5" width="695.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">■</text><text class="terminal-r1" x="24.4" y="20" textLength="256.2" clip-path="url(#terminal-line-0)">GitHub authenticated.</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
|
@ -0,0 +1,94 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 294.0" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #d9d9d9 }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="243.0" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="292" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportMessageAuthRequiredApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="1.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="585.6" y="1.5" width="390.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="25.9" width="451.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="475.8" y="25.9" width="500.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">■</text><text class="terminal-r1" x="24.4" y="20" textLength="561.2" clip-path="url(#terminal-line-0)">GitHub auth required. Code: ABCD-1234 (copied)</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="24.4" y="44.4" textLength="451.4" clip-path="url(#terminal-line-1)">Open: https://github.com/login/device</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
|
@ -0,0 +1,94 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 294.0" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #d9d9d9 }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="243.0" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="292" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportMessageCheckingGitApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="1.5" width="268.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="292.8" y="1.5" width="683.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">■</text><text class="terminal-r1" x="24.4" y="20" textLength="268.4" clip-path="url(#terminal-line-0)">Checking git status...</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
|
@ -0,0 +1,95 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 294.0" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #98e024 }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="243.0" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="292" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportMessageCompleteApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="1.5" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="573.4" y="1.5" width="402.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">✓</text><text class="terminal-r3" x="24.4" y="20" textLength="549" clip-path="url(#terminal-line-0)">Teleported to Nuage: https://chat.example.com</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
|
|
@ -0,0 +1,95 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 294.0" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #f4005f }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
.terminal-r3 { fill: #d9d9d9 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="243.0" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="292" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportMessageErrorApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="1.5" width="671" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="695.4" y="1.5" width="280.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">✕</text><text class="terminal-r3" x="24.4" y="20" textLength="671" clip-path="url(#terminal-line-0)">Teleport failed: Git repository has uncommitted changes</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
|
|
@ -0,0 +1,94 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 294.0" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #d9d9d9 }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="243.0" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="292" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportMessagePushingApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="1.5" width="244" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="268.4" y="1.5" width="707.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">■</text><text class="terminal-r1" x="24.4" y="20" textLength="244" clip-path="url(#terminal-line-0)">Pushing to remote...</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
|
@ -0,0 +1,94 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 294.0" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #d9d9d9 }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="243.0" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="292" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportMessageSendingTokenApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="1.5" width="402.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="427" y="1.5" width="549" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">■</text><text class="terminal-r1" x="24.4" y="20" textLength="402.6" clip-path="url(#terminal-line-0)">Sending encrypted GitHub token...</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
|
@ -0,0 +1,94 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 994 294.0" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #d9d9d9 }
|
||||
.terminal-r2 { fill: #c5c8c6 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="975.0" height="243.0" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="976" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="292" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TeleportMessageStartingWorkflowApp</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
<rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="12.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="1.5" width="317.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="341.6" y="1.5" width="634.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/>
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">■</text><text class="terminal-r1" x="24.4" y="20" textLength="317.2" clip-path="url(#terminal-line-0)">Starting Nuage workflow...</text><text class="terminal-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
|
@ -0,0 +1,202 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 1482 928.4" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #c5c8c6 }
|
||||
.terminal-r2 { fill: #ff8205;font-weight: bold }
|
||||
.terminal-r3 { fill: #68a0b3 }
|
||||
.terminal-r4 { fill: #ff8205 }
|
||||
.terminal-r5 { fill: #608ab1;font-weight: bold }
|
||||
.terminal-r6 { fill: #9a9b99 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="1463.0" height="877.4" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-9">
|
||||
<rect x="0" y="221.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-10">
|
||||
<rect x="0" y="245.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-11">
|
||||
<rect x="0" y="269.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-12">
|
||||
<rect x="0" y="294.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-13">
|
||||
<rect x="0" y="318.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-14">
|
||||
<rect x="0" y="343.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-15">
|
||||
<rect x="0" y="367.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-16">
|
||||
<rect x="0" y="391.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-17">
|
||||
<rect x="0" y="416.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-18">
|
||||
<rect x="0" y="440.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-19">
|
||||
<rect x="0" y="465.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-20">
|
||||
<rect x="0" y="489.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-21">
|
||||
<rect x="0" y="513.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-22">
|
||||
<rect x="0" y="538.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-23">
|
||||
<rect x="0" y="562.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-24">
|
||||
<rect x="0" y="587.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-25">
|
||||
<rect x="0" y="611.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-26">
|
||||
<rect x="0" y="635.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-27">
|
||||
<rect x="0" y="660.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-28">
|
||||
<rect x="0" y="684.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-29">
|
||||
<rect x="0" y="709.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-30">
|
||||
<rect x="0" y="733.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-31">
|
||||
<rect x="0" y="757.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-32">
|
||||
<rect x="0" y="782.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-33">
|
||||
<rect x="0" y="806.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-34">
|
||||
<rect x="0" y="831.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="926.4" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">SnapshotTestAppWithWhatsNewNoPlanCTA</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-line-0)">  ⡠⣒⠄  ⡔⢄⠔⡄</text><text class="terminal-r2" x="170.8" y="20" textLength="146.4" clip-path="url(#terminal-line-0)">Mistral Vibe</text><text class="terminal-r1" x="317.2" y="20" textLength="122" clip-path="url(#terminal-line-0)"> v2.1.0 · </text><text class="terminal-r3" x="439.2" y="20" textLength="183" clip-path="url(#terminal-line-0)">devstral-latest</text><text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="134.2" clip-path="url(#terminal-line-1)"> ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣</text><text class="terminal-r1" x="170.8" y="44.4" textLength="414.8" clip-path="url(#terminal-line-1)">1 model · 0 MCP servers · 0 skills</text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="134.2" clip-path="url(#terminal-line-2)">  ⠉⠒⠣⠤⠵⠤⠬⠮⠆</text><text class="terminal-r1" x="170.8" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">Type </text><text class="terminal-r3" x="231.8" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">/help</text><text class="terminal-r1" x="292.8" y="68.8" textLength="256.2" clip-path="url(#terminal-line-2)"> for more information</text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r4" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">┃</text><text class="terminal-r5" x="24.4" y="117.6" textLength="122" clip-path="url(#terminal-line-4)">What's New</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r4" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">┃</text><text class="terminal-r1" x="24.4" y="142" textLength="134.2" clip-path="url(#terminal-line-5)">• Feature 1</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r4" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">┃</text><text class="terminal-r1" x="24.4" y="166.4" textLength="134.2" clip-path="url(#terminal-line-6)">• Feature 2</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r4" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">┃</text><text class="terminal-r1" x="24.4" y="190.8" textLength="134.2" clip-path="url(#terminal-line-7)">• Feature 3</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r1" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r1" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r1" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
</text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
|
||||
</text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
|
||||
</text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
|
||||
</text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
|
||||
</text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
|
||||
</text><text class="terminal-r1" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)">
|
||||
</text><text class="terminal-r1" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-line-20)">
|
||||
</text><text class="terminal-r1" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)">
|
||||
</text><text class="terminal-r1" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)">
|
||||
</text><text class="terminal-r1" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)">
|
||||
</text><text class="terminal-r1" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)">
|
||||
</text><text class="terminal-r1" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-line-25)">
|
||||
</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
|
||||
</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
|
||||
</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
|
||||
</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
|
||||
</text><text class="terminal-r6" x="0" y="752" textLength="1464" clip-path="url(#terminal-line-30)">┌──────────────────────────────────────────────────────────────────────────────────────────────────────────── default ─┐</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
|
||||
</text><text class="terminal-r6" x="0" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">│</text><text class="terminal-r2" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">></text><text class="terminal-r6" x="1451.8" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">│</text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
|
||||
</text><text class="terminal-r6" x="0" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">│</text><text class="terminal-r6" x="1451.8" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">│</text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">
|
||||
</text><text class="terminal-r6" x="0" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">│</text><text class="terminal-r6" x="1451.8" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">│</text><text class="terminal-r1" x="1464" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">
|
||||
</text><text class="terminal-r6" x="0" y="849.6" textLength="1464" clip-path="url(#terminal-line-34)">└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r1" x="1464" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)">
|
||||
</text><text class="terminal-r6" x="0" y="874" textLength="158.6" clip-path="url(#terminal-line-35)">/test/workdir</text><text class="terminal-r6" x="1256.6" y="874" textLength="207.4" clip-path="url(#terminal-line-35)">0% of 200k tokens</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -0,0 +1,203 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 1482 928.4" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #c5c8c6 }
|
||||
.terminal-r2 { fill: #ff8205;font-weight: bold }
|
||||
.terminal-r3 { fill: #68a0b3 }
|
||||
.terminal-r4 { fill: #ff8205 }
|
||||
.terminal-r5 { fill: #608ab1;font-weight: bold }
|
||||
.terminal-r6 { fill: #c5c8c6;font-weight: bold;text-decoration: underline; }
|
||||
.terminal-r7 { fill: #9a9b99 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="1463.0" height="877.4" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-9">
|
||||
<rect x="0" y="221.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-10">
|
||||
<rect x="0" y="245.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-11">
|
||||
<rect x="0" y="269.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-12">
|
||||
<rect x="0" y="294.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-13">
|
||||
<rect x="0" y="318.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-14">
|
||||
<rect x="0" y="343.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-15">
|
||||
<rect x="0" y="367.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-16">
|
||||
<rect x="0" y="391.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-17">
|
||||
<rect x="0" y="416.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-18">
|
||||
<rect x="0" y="440.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-19">
|
||||
<rect x="0" y="465.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-20">
|
||||
<rect x="0" y="489.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-21">
|
||||
<rect x="0" y="513.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-22">
|
||||
<rect x="0" y="538.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-23">
|
||||
<rect x="0" y="562.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-24">
|
||||
<rect x="0" y="587.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-25">
|
||||
<rect x="0" y="611.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-26">
|
||||
<rect x="0" y="635.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-27">
|
||||
<rect x="0" y="660.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-28">
|
||||
<rect x="0" y="684.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-29">
|
||||
<rect x="0" y="709.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-30">
|
||||
<rect x="0" y="733.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-31">
|
||||
<rect x="0" y="757.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-32">
|
||||
<rect x="0" y="782.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-33">
|
||||
<rect x="0" y="806.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-34">
|
||||
<rect x="0" y="831.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="926.4" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">SnapshotTestAppWithSwitchKeyCTA</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-line-0)">  ⡠⣒⠄  ⡔⢄⠔⡄</text><text class="terminal-r2" x="170.8" y="20" textLength="146.4" clip-path="url(#terminal-line-0)">Mistral Vibe</text><text class="terminal-r1" x="317.2" y="20" textLength="122" clip-path="url(#terminal-line-0)"> v2.1.0 · </text><text class="terminal-r3" x="439.2" y="20" textLength="183" clip-path="url(#terminal-line-0)">devstral-latest</text><text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="134.2" clip-path="url(#terminal-line-1)"> ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣</text><text class="terminal-r1" x="170.8" y="44.4" textLength="414.8" clip-path="url(#terminal-line-1)">1 model · 0 MCP servers · 0 skills</text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="134.2" clip-path="url(#terminal-line-2)">  ⠉⠒⠣⠤⠵⠤⠬⠮⠆</text><text class="terminal-r1" x="170.8" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">Type </text><text class="terminal-r3" x="231.8" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">/help</text><text class="terminal-r1" x="292.8" y="68.8" textLength="256.2" clip-path="url(#terminal-line-2)"> for more information</text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r4" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">┃</text><text class="terminal-r5" x="24.4" y="117.6" textLength="122" clip-path="url(#terminal-line-4)">What's New</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r4" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">┃</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r4" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">┃</text><text class="terminal-r1" x="24.4" y="166.4" textLength="134.2" clip-path="url(#terminal-line-6)">• Feature 1</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r4" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">┃</text><text class="terminal-r1" x="24.4" y="190.8" textLength="134.2" clip-path="url(#terminal-line-7)">• Feature 2</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r4" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">┃</text><text class="terminal-r1" x="24.4" y="215.2" textLength="134.2" clip-path="url(#terminal-line-8)">• Feature 3</text><text class="terminal-r1" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r4" x="0" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">┃</text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r4" x="0" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">┃</text><text class="terminal-r5" x="24.4" y="264" textLength="183" clip-path="url(#terminal-line-10)">Switch to your </text><text class="terminal-r6" x="207.4" y="264" textLength="231.8" clip-path="url(#terminal-line-10)">Le Chat Pro API key</text><text class="terminal-r1" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r1" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
</text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
|
||||
</text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
|
||||
</text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
|
||||
</text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
|
||||
</text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
|
||||
</text><text class="terminal-r1" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)">
|
||||
</text><text class="terminal-r1" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-line-20)">
|
||||
</text><text class="terminal-r1" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)">
|
||||
</text><text class="terminal-r1" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)">
|
||||
</text><text class="terminal-r1" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)">
|
||||
</text><text class="terminal-r1" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)">
|
||||
</text><text class="terminal-r1" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-line-25)">
|
||||
</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
|
||||
</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
|
||||
</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
|
||||
</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
|
||||
</text><text class="terminal-r7" x="0" y="752" textLength="1464" clip-path="url(#terminal-line-30)">┌──────────────────────────────────────────────────────────────────────────────────────────────────────────── default ─┐</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
|
||||
</text><text class="terminal-r7" x="0" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">│</text><text class="terminal-r2" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">></text><text class="terminal-r7" x="1451.8" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">│</text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
|
||||
</text><text class="terminal-r7" x="0" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">│</text><text class="terminal-r7" x="1451.8" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">│</text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">
|
||||
</text><text class="terminal-r7" x="0" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">│</text><text class="terminal-r7" x="1451.8" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">│</text><text class="terminal-r1" x="1464" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">
|
||||
</text><text class="terminal-r7" x="0" y="849.6" textLength="1464" clip-path="url(#terminal-line-34)">└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r1" x="1464" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)">
|
||||
</text><text class="terminal-r7" x="0" y="874" textLength="158.6" clip-path="url(#terminal-line-35)">/test/workdir</text><text class="terminal-r7" x="1256.6" y="874" textLength="207.4" clip-path="url(#terminal-line-35)">0% of 200k tokens</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -0,0 +1,203 @@
|
|||
<svg class="rich-terminal" viewBox="0 0 1482 928.4" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Generated with Rich https://www.textualize.io -->
|
||||
<style>
|
||||
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Regular"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Fira Code";
|
||||
src: local("FiraCode-Bold"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
|
||||
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
|
||||
font-style: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.terminal-matrix {
|
||||
font-family: Fira Code, monospace;
|
||||
font-size: 20px;
|
||||
line-height: 24.4px;
|
||||
font-variant-east-asian: full-width;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
.terminal-r1 { fill: #c5c8c6 }
|
||||
.terminal-r2 { fill: #ff8205;font-weight: bold }
|
||||
.terminal-r3 { fill: #68a0b3 }
|
||||
.terminal-r4 { fill: #ff8205 }
|
||||
.terminal-r5 { fill: #608ab1;font-weight: bold }
|
||||
.terminal-r6 { fill: #c5c8c6;font-weight: bold;text-decoration: underline; }
|
||||
.terminal-r7 { fill: #9a9b99 }
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<clipPath id="terminal-clip-terminal">
|
||||
<rect x="0" y="0" width="1463.0" height="877.4" />
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-0">
|
||||
<rect x="0" y="1.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-1">
|
||||
<rect x="0" y="25.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-2">
|
||||
<rect x="0" y="50.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-3">
|
||||
<rect x="0" y="74.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-4">
|
||||
<rect x="0" y="99.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-5">
|
||||
<rect x="0" y="123.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-6">
|
||||
<rect x="0" y="147.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-7">
|
||||
<rect x="0" y="172.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-8">
|
||||
<rect x="0" y="196.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-9">
|
||||
<rect x="0" y="221.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-10">
|
||||
<rect x="0" y="245.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-11">
|
||||
<rect x="0" y="269.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-12">
|
||||
<rect x="0" y="294.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-13">
|
||||
<rect x="0" y="318.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-14">
|
||||
<rect x="0" y="343.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-15">
|
||||
<rect x="0" y="367.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-16">
|
||||
<rect x="0" y="391.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-17">
|
||||
<rect x="0" y="416.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-18">
|
||||
<rect x="0" y="440.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-19">
|
||||
<rect x="0" y="465.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-20">
|
||||
<rect x="0" y="489.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-21">
|
||||
<rect x="0" y="513.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-22">
|
||||
<rect x="0" y="538.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-23">
|
||||
<rect x="0" y="562.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-24">
|
||||
<rect x="0" y="587.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-25">
|
||||
<rect x="0" y="611.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-26">
|
||||
<rect x="0" y="635.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-27">
|
||||
<rect x="0" y="660.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-28">
|
||||
<rect x="0" y="684.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-29">
|
||||
<rect x="0" y="709.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-30">
|
||||
<rect x="0" y="733.5" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-31">
|
||||
<rect x="0" y="757.9" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-32">
|
||||
<rect x="0" y="782.3" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-33">
|
||||
<rect x="0" y="806.7" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
<clipPath id="terminal-line-34">
|
||||
<rect x="0" y="831.1" width="1464" height="24.65"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="926.4" rx="8"/><text class="terminal-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">SnapshotTestAppWithPlanUpgradeCTA</text>
|
||||
<g transform="translate(26,22)">
|
||||
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
|
||||
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
|
||||
<circle cx="44" cy="0" r="7" fill="#28c840"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(9, 41)" clip-path="url(#terminal-clip-terminal)">
|
||||
|
||||
<g class="terminal-matrix">
|
||||
<text class="terminal-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-line-0)">  ⡠⣒⠄  ⡔⢄⠔⡄</text><text class="terminal-r2" x="170.8" y="20" textLength="146.4" clip-path="url(#terminal-line-0)">Mistral Vibe</text><text class="terminal-r1" x="317.2" y="20" textLength="122" clip-path="url(#terminal-line-0)"> v2.1.0 · </text><text class="terminal-r3" x="439.2" y="20" textLength="183" clip-path="url(#terminal-line-0)">devstral-latest</text><text class="terminal-r1" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-line-0)">
|
||||
</text><text class="terminal-r1" x="0" y="44.4" textLength="134.2" clip-path="url(#terminal-line-1)"> ⢸⠸⣀⡔⢉⠱⣃⡢⣂⡣</text><text class="terminal-r1" x="170.8" y="44.4" textLength="414.8" clip-path="url(#terminal-line-1)">1 model · 0 MCP servers · 0 skills</text><text class="terminal-r1" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-line-1)">
|
||||
</text><text class="terminal-r1" x="0" y="68.8" textLength="134.2" clip-path="url(#terminal-line-2)">  ⠉⠒⠣⠤⠵⠤⠬⠮⠆</text><text class="terminal-r1" x="170.8" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">Type </text><text class="terminal-r3" x="231.8" y="68.8" textLength="61" clip-path="url(#terminal-line-2)">/help</text><text class="terminal-r1" x="292.8" y="68.8" textLength="256.2" clip-path="url(#terminal-line-2)"> for more information</text><text class="terminal-r1" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-line-2)">
|
||||
</text><text class="terminal-r1" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-line-3)">
|
||||
</text><text class="terminal-r4" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">┃</text><text class="terminal-r5" x="24.4" y="117.6" textLength="122" clip-path="url(#terminal-line-4)">What's New</text><text class="terminal-r1" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-line-4)">
|
||||
</text><text class="terminal-r4" x="0" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">┃</text><text class="terminal-r1" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-line-5)">
|
||||
</text><text class="terminal-r4" x="0" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">┃</text><text class="terminal-r1" x="24.4" y="166.4" textLength="134.2" clip-path="url(#terminal-line-6)">• Feature 1</text><text class="terminal-r1" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-line-6)">
|
||||
</text><text class="terminal-r4" x="0" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">┃</text><text class="terminal-r1" x="24.4" y="190.8" textLength="134.2" clip-path="url(#terminal-line-7)">• Feature 2</text><text class="terminal-r1" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-line-7)">
|
||||
</text><text class="terminal-r4" x="0" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">┃</text><text class="terminal-r1" x="24.4" y="215.2" textLength="134.2" clip-path="url(#terminal-line-8)">• Feature 3</text><text class="terminal-r1" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-line-8)">
|
||||
</text><text class="terminal-r4" x="0" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">┃</text><text class="terminal-r1" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-line-9)">
|
||||
</text><text class="terminal-r4" x="0" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">┃</text><text class="terminal-r5" x="24.4" y="264" textLength="292.8" clip-path="url(#terminal-line-10)">Unlock more with Vibe - </text><text class="terminal-r6" x="317.2" y="264" textLength="268.4" clip-path="url(#terminal-line-10)">Upgrade to Le Chat Pro</text><text class="terminal-r1" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-line-10)">
|
||||
</text><text class="terminal-r1" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-line-11)">
|
||||
</text><text class="terminal-r1" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-line-12)">
|
||||
</text><text class="terminal-r1" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-line-13)">
|
||||
</text><text class="terminal-r1" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-line-14)">
|
||||
</text><text class="terminal-r1" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-line-15)">
|
||||
</text><text class="terminal-r1" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-line-16)">
|
||||
</text><text class="terminal-r1" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-line-17)">
|
||||
</text><text class="terminal-r1" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-line-18)">
|
||||
</text><text class="terminal-r1" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-line-19)">
|
||||
</text><text class="terminal-r1" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-line-20)">
|
||||
</text><text class="terminal-r1" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-line-21)">
|
||||
</text><text class="terminal-r1" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-line-22)">
|
||||
</text><text class="terminal-r1" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-line-23)">
|
||||
</text><text class="terminal-r1" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-line-24)">
|
||||
</text><text class="terminal-r1" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-line-25)">
|
||||
</text><text class="terminal-r1" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-line-26)">
|
||||
</text><text class="terminal-r1" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-line-27)">
|
||||
</text><text class="terminal-r1" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-line-28)">
|
||||
</text><text class="terminal-r1" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-line-29)">
|
||||
</text><text class="terminal-r7" x="0" y="752" textLength="1464" clip-path="url(#terminal-line-30)">┌──────────────────────────────────────────────────────────────────────────────────────────────────────────── default ─┐</text><text class="terminal-r1" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-line-30)">
|
||||
</text><text class="terminal-r7" x="0" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">│</text><text class="terminal-r2" x="24.4" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">></text><text class="terminal-r7" x="1451.8" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">│</text><text class="terminal-r1" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-line-31)">
|
||||
</text><text class="terminal-r7" x="0" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">│</text><text class="terminal-r7" x="1451.8" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">│</text><text class="terminal-r1" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-line-32)">
|
||||
</text><text class="terminal-r7" x="0" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">│</text><text class="terminal-r7" x="1451.8" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">│</text><text class="terminal-r1" x="1464" y="825.2" textLength="12.2" clip-path="url(#terminal-line-33)">
|
||||
</text><text class="terminal-r7" x="0" y="849.6" textLength="1464" clip-path="url(#terminal-line-34)">└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘</text><text class="terminal-r1" x="1464" y="849.6" textLength="12.2" clip-path="url(#terminal-line-34)">
|
||||
</text><text class="terminal-r7" x="0" y="874" textLength="158.6" clip-path="url(#terminal-line-35)">/test/workdir</text><text class="terminal-r7" x="1256.6" y="874" textLength="207.4" clip-path="url(#terminal-line-35)">0% of 200k tokens</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 14 KiB |
|
|
@ -4,28 +4,25 @@ from rich.style import Style
|
|||
from textual.widgets.text_area import TextAreaTheme
|
||||
|
||||
from tests.cli.plan_offer.adapters.fake_whoami_gateway import FakeWhoAmIGateway
|
||||
from tests.conftest import build_test_agent_loop, build_test_vibe_config
|
||||
from tests.stubs.fake_backend import FakeBackend
|
||||
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIResponse
|
||||
from vibe.cli.textual_ui.app import VibeApp
|
||||
from vibe.cli.textual_ui.widgets.chat_input import ChatTextArea
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.agents.models import BuiltinAgentName
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
from vibe.core.config import VibeConfig
|
||||
|
||||
|
||||
def default_config() -> VibeConfig:
|
||||
"""Default configuration for snapshot testing.
|
||||
Remove as much interference as possible from the snapshot comparison, in order to get a clean pixel-to-pixel comparison.
|
||||
- Injects a fake backend to prevent (or stub) LLM calls.
|
||||
- Disables the welcome banner animation.
|
||||
- Disables the banner animation.
|
||||
- Forces a value for the displayed workdir
|
||||
- Hides the chat input cursor (as the blinking animation is not deterministic).
|
||||
"""
|
||||
return VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False),
|
||||
textual_theme="gruvbox",
|
||||
disable_welcome_banner_animation=True,
|
||||
displayed_workdir="/test/workdir",
|
||||
enable_update_checks=False,
|
||||
return build_test_vibe_config(
|
||||
disable_welcome_banner_animation=True, displayed_workdir="/test/workdir"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -39,16 +36,23 @@ class BaseSnapshotTestApp(VibeApp):
|
|||
backend: FakeBackend | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
config = config or default_config()
|
||||
|
||||
agent_loop = AgentLoop(
|
||||
config,
|
||||
agent_loop = build_test_agent_loop(
|
||||
config=config or default_config(),
|
||||
agent_name=self._current_agent_name,
|
||||
enable_streaming=kwargs.get("enable_streaming", False),
|
||||
enable_streaming=bool(kwargs.get("enable_streaming", False)),
|
||||
backend=backend or FakeBackend(),
|
||||
)
|
||||
|
||||
plan_offer_gateway = kwargs.pop("plan_offer_gateway", FakeWhoAmIGateway())
|
||||
plan_offer_gateway = kwargs.pop(
|
||||
"plan_offer_gateway",
|
||||
FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=True,
|
||||
advertise_pro_plan=False,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
agent_loop=agent_loop, plan_offer_gateway=plan_offer_gateway, **kwargs
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
|
||||
from textual.pilot import Pilot
|
||||
|
||||
from tests.cli.plan_offer.adapters.fake_whoami_gateway import FakeWhoAmIGateway
|
||||
from tests.snapshots.base_snapshot_test_app import BaseSnapshotTestApp, default_config
|
||||
from tests.snapshots.snap_compare import SnapCompare
|
||||
from tests.update_notifier.adapters.fake_update_cache_repository import (
|
||||
FakeUpdateCacheRepository,
|
||||
)
|
||||
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIResponse
|
||||
from vibe.cli.update_notifier import UpdateCache
|
||||
|
||||
|
||||
class PlanOfferSnapshotApp(BaseSnapshotTestApp):
|
||||
def __init__(self, gateway: FakeWhoAmIGateway):
|
||||
self._previous_api_key = os.environ.get("MISTRAL_API_KEY")
|
||||
os.environ["MISTRAL_API_KEY"] = "snapshot-api-key"
|
||||
super().__init__(
|
||||
config=default_config(),
|
||||
plan_offer_gateway=gateway,
|
||||
update_cache_repository=FakeUpdateCacheRepository(),
|
||||
)
|
||||
|
||||
def on_unmount(self) -> None:
|
||||
if self._previous_api_key is None:
|
||||
os.environ.pop("MISTRAL_API_KEY", None)
|
||||
else:
|
||||
os.environ["MISTRAL_API_KEY"] = self._previous_api_key
|
||||
return None
|
||||
|
||||
|
||||
class SnapshotAppPlanOfferUpgrade(PlanOfferSnapshotApp):
|
||||
def __init__(self) -> None:
|
||||
gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=False,
|
||||
advertise_pro_plan=True,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
super().__init__(gateway=gateway)
|
||||
|
||||
|
||||
class SnapshotAppPlanOfferSwitchKey(PlanOfferSnapshotApp):
|
||||
def __init__(self) -> None:
|
||||
gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=False,
|
||||
advertise_pro_plan=False,
|
||||
prompt_switching_to_pro_plan=True,
|
||||
)
|
||||
)
|
||||
super().__init__(gateway=gateway)
|
||||
|
||||
|
||||
class SnapshotAppPlanOfferNone(PlanOfferSnapshotApp):
|
||||
def __init__(self) -> None:
|
||||
gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=True,
|
||||
advertise_pro_plan=False,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
super().__init__(gateway=gateway)
|
||||
|
||||
|
||||
class SnapshotAppWhatsNewAndPlanOffer(PlanOfferSnapshotApp):
|
||||
def __init__(self) -> None:
|
||||
gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=False,
|
||||
advertise_pro_plan=True,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
super().__init__(gateway=gateway)
|
||||
cache = UpdateCache(
|
||||
latest_version="1.0.0",
|
||||
stored_at_timestamp=int(time.time()),
|
||||
seen_whats_new_version=None,
|
||||
)
|
||||
self._update_cache_repository = FakeUpdateCacheRepository(update_cache=cache)
|
||||
self._current_version = "1.0.0"
|
||||
|
||||
|
||||
async def _pause_for_plan_offer_task(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.1)
|
||||
|
||||
|
||||
def test_snapshot_shows_upgrade_plan_offer(snap_compare: SnapCompare) -> None:
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_plan_offer.py:SnapshotAppPlanOfferUpgrade",
|
||||
terminal_size=(120, 36),
|
||||
run_before=_pause_for_plan_offer_task,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_shows_switch_key_plan_offer(snap_compare: SnapCompare) -> None:
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_plan_offer.py:SnapshotAppPlanOfferSwitchKey",
|
||||
terminal_size=(120, 36),
|
||||
run_before=_pause_for_plan_offer_task,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_shows_no_plan_offer(snap_compare: SnapCompare) -> None:
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_plan_offer.py:SnapshotAppPlanOfferNone",
|
||||
terminal_size=(120, 36),
|
||||
run_before=_pause_for_plan_offer_task,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_shows_whats_new_and_plan_offer(
|
||||
snap_compare: SnapCompare, tmp_path: Path
|
||||
) -> None:
|
||||
whats_new_file = tmp_path / "whats_new.md"
|
||||
whats_new_file.write_text("# What's New\n\n- Feature 1\n- Feature 2")
|
||||
|
||||
with patch("vibe.cli.update_notifier.whats_new.VIBE_ROOT", tmp_path):
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_plan_offer.py:SnapshotAppWhatsNewAndPlanOffer",
|
||||
terminal_size=(120, 36),
|
||||
run_before=_pause_for_plan_offer_task,
|
||||
)
|
||||
|
|
@ -2,12 +2,12 @@ from __future__ import annotations
|
|||
|
||||
from textual.pilot import Pilot
|
||||
|
||||
from tests.conftest import build_test_agent_loop
|
||||
from tests.mock.utils import mock_llm_chunk
|
||||
from tests.snapshots.base_snapshot_test_app import BaseSnapshotTestApp, default_config
|
||||
from tests.snapshots.snap_compare import SnapCompare
|
||||
from tests.stubs.fake_backend import FakeBackend
|
||||
from vibe.cli.textual_ui.widgets.messages import ReasoningMessage
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
|
||||
|
||||
class SnapshotTestAppWithReasoningContent(BaseSnapshotTestApp):
|
||||
|
|
@ -31,8 +31,8 @@ class SnapshotTestAppWithReasoningContent(BaseSnapshotTestApp):
|
|||
]
|
||||
)
|
||||
super().__init__(config=config)
|
||||
self.agent_loop = AgentLoop(
|
||||
config,
|
||||
self.agent_loop = build_test_agent_loop(
|
||||
config=config,
|
||||
agent_name=self._current_agent_name,
|
||||
enable_streaming=True,
|
||||
backend=fake_backend,
|
||||
|
|
@ -57,8 +57,8 @@ class SnapshotTestAppWithInterleavedReasoning(BaseSnapshotTestApp):
|
|||
]
|
||||
)
|
||||
super().__init__(config=config)
|
||||
self.agent_loop = AgentLoop(
|
||||
config,
|
||||
self.agent_loop = build_test_agent_loop(
|
||||
config=config,
|
||||
agent_name=self._current_agent_name,
|
||||
enable_streaming=True,
|
||||
backend=fake_backend,
|
||||
|
|
@ -123,8 +123,8 @@ class SnapshotTestAppWithBufferedReasoningTransition(BaseSnapshotTestApp):
|
|||
]
|
||||
)
|
||||
super().__init__(config=config)
|
||||
self.agent_loop = AgentLoop(
|
||||
config,
|
||||
self.agent_loop = build_test_agent_loop(
|
||||
config=config,
|
||||
agent_name=self._current_agent_name,
|
||||
enable_streaming=True,
|
||||
backend=fake_backend,
|
||||
|
|
|
|||
249
tests/snapshots/test_ui_snapshot_teleport.py
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Container
|
||||
from textual.pilot import Pilot
|
||||
|
||||
from tests.snapshots.snap_compare import SnapCompare
|
||||
from vibe.cli.textual_ui.widgets.question_app import QuestionApp
|
||||
from vibe.cli.textual_ui.widgets.teleport_message import TeleportMessage
|
||||
from vibe.core.tools.builtins.ask_user_question import (
|
||||
AskUserQuestionArgs,
|
||||
Choice,
|
||||
Question,
|
||||
)
|
||||
|
||||
|
||||
class TeleportMessageTestApp(App):
|
||||
CSS_PATH = "../../vibe/cli/textual_ui/app.tcss"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Container():
|
||||
yield TeleportMessage()
|
||||
|
||||
|
||||
class TeleportMessageCheckingGitApp(TeleportMessageTestApp):
|
||||
def on_mount(self) -> None:
|
||||
widget = self.query_one(TeleportMessage)
|
||||
widget.set_status("Checking git status...")
|
||||
if widget._spinner_timer:
|
||||
widget._spinner_timer.stop()
|
||||
|
||||
|
||||
class TeleportMessagePushingApp(TeleportMessageTestApp):
|
||||
def on_mount(self) -> None:
|
||||
widget = self.query_one(TeleportMessage)
|
||||
widget.set_status("Pushing to remote...")
|
||||
if widget._spinner_timer:
|
||||
widget._spinner_timer.stop()
|
||||
|
||||
|
||||
class TeleportMessageAuthRequiredApp(TeleportMessageTestApp):
|
||||
def on_mount(self) -> None:
|
||||
widget = self.query_one(TeleportMessage)
|
||||
widget.set_status(
|
||||
"GitHub auth required. Code: ABCD-1234 (copied)\nOpen: https://github.com/login/device"
|
||||
)
|
||||
if widget._spinner_timer:
|
||||
widget._spinner_timer.stop()
|
||||
|
||||
|
||||
class TeleportMessageAuthCompleteApp(TeleportMessageTestApp):
|
||||
def on_mount(self) -> None:
|
||||
widget = self.query_one(TeleportMessage)
|
||||
widget.set_status("GitHub authenticated.")
|
||||
if widget._spinner_timer:
|
||||
widget._spinner_timer.stop()
|
||||
|
||||
|
||||
class TeleportMessageStartingWorkflowApp(TeleportMessageTestApp):
|
||||
def on_mount(self) -> None:
|
||||
widget = self.query_one(TeleportMessage)
|
||||
widget.set_status("Starting Nuage workflow...")
|
||||
if widget._spinner_timer:
|
||||
widget._spinner_timer.stop()
|
||||
|
||||
|
||||
class TeleportMessageSendingTokenApp(TeleportMessageTestApp):
|
||||
def on_mount(self) -> None:
|
||||
widget = self.query_one(TeleportMessage)
|
||||
widget.set_status("Sending encrypted GitHub token...")
|
||||
if widget._spinner_timer:
|
||||
widget._spinner_timer.stop()
|
||||
|
||||
|
||||
class TeleportMessageCompleteApp(TeleportMessageTestApp):
|
||||
def on_mount(self) -> None:
|
||||
widget = self.query_one(TeleportMessage)
|
||||
widget.set_complete("https://chat.example.com")
|
||||
|
||||
|
||||
class TeleportMessageErrorApp(TeleportMessageTestApp):
|
||||
def on_mount(self) -> None:
|
||||
widget = self.query_one(TeleportMessage)
|
||||
widget.set_error("Git repository has uncommitted changes")
|
||||
|
||||
|
||||
def _push_confirmation_args(count: int) -> AskUserQuestionArgs:
|
||||
word = f"commit{'s' if count != 1 else ''}"
|
||||
return AskUserQuestionArgs(
|
||||
questions=[
|
||||
Question(
|
||||
question=f"You have {count} unpushed {word}. Push to continue?",
|
||||
header="Push",
|
||||
options=[Choice(label="Push and continue"), Choice(label="Cancel")],
|
||||
hide_other=True,
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class TeleportPushConfirmationTestApp(App):
|
||||
CSS_PATH = "../../vibe/cli/textual_ui/app.tcss"
|
||||
|
||||
def __init__(self, count: int = 3):
|
||||
super().__init__()
|
||||
self.count = count
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with Container(id="bottom-app-container"):
|
||||
yield QuestionApp(args=_push_confirmation_args(self.count))
|
||||
|
||||
|
||||
class TeleportPushConfirmationSingleCommitApp(TeleportPushConfirmationTestApp):
|
||||
def __init__(self):
|
||||
super().__init__(count=1)
|
||||
|
||||
|
||||
class TeleportPushConfirmationMultipleCommitsApp(TeleportPushConfirmationTestApp):
|
||||
def __init__(self):
|
||||
super().__init__(count=5)
|
||||
|
||||
|
||||
def test_snapshot_teleport_status_checking_git(snap_compare: SnapCompare) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportMessageCheckingGitApp",
|
||||
terminal_size=(80, 10),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_status_pushing(snap_compare: SnapCompare) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportMessagePushingApp",
|
||||
terminal_size=(80, 10),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_status_auth_required(snap_compare: SnapCompare) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportMessageAuthRequiredApp",
|
||||
terminal_size=(80, 10),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_status_auth_complete(snap_compare: SnapCompare) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportMessageAuthCompleteApp",
|
||||
terminal_size=(80, 10),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_status_starting_workflow(snap_compare: SnapCompare) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportMessageStartingWorkflowApp",
|
||||
terminal_size=(80, 10),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_status_sending_token(snap_compare: SnapCompare) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportMessageSendingTokenApp",
|
||||
terminal_size=(80, 10),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_status_complete(snap_compare: SnapCompare) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportMessageCompleteApp",
|
||||
terminal_size=(80, 10),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_status_error(snap_compare: SnapCompare) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportMessageErrorApp",
|
||||
terminal_size=(80, 10),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_push_confirmation_single_commit(
|
||||
snap_compare: SnapCompare,
|
||||
) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.1)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportPushConfirmationSingleCommitApp",
|
||||
terminal_size=(80, 12),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_push_confirmation_multiple_commits(
|
||||
snap_compare: SnapCompare,
|
||||
) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.1)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportPushConfirmationMultipleCommitsApp",
|
||||
terminal_size=(80, 12),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_teleport_push_confirmation_cancel_selected(
|
||||
snap_compare: SnapCompare,
|
||||
) -> None:
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.1)
|
||||
await pilot.press("down")
|
||||
await pilot.pause(0.1)
|
||||
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_teleport.py:TeleportPushConfirmationMultipleCommitsApp",
|
||||
terminal_size=(80, 12),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
|
@ -1,22 +1,28 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
|
||||
from textual.pilot import Pilot
|
||||
|
||||
from tests.cli.plan_offer.adapters.fake_whoami_gateway import FakeWhoAmIGateway
|
||||
from tests.snapshots.base_snapshot_test_app import BaseSnapshotTestApp, default_config
|
||||
from tests.snapshots.snap_compare import SnapCompare
|
||||
from tests.update_notifier.adapters.fake_update_cache_repository import (
|
||||
FakeUpdateCacheRepository,
|
||||
)
|
||||
from tests.update_notifier.adapters.fake_update_gateway import FakeUpdateGateway
|
||||
from vibe.cli.plan_offer.ports.whoami_gateway import WhoAmIResponse
|
||||
from vibe.cli.update_notifier import UpdateCache
|
||||
|
||||
|
||||
class SnapshotTestAppWithWhatsNew(BaseSnapshotTestApp):
|
||||
def __init__(self):
|
||||
def __init__(self, gateway: FakeWhoAmIGateway | None = None):
|
||||
self._previous_api_key = os.environ.get("MISTRAL_API_KEY")
|
||||
os.environ["MISTRAL_API_KEY"] = "snapshot-api-key"
|
||||
|
||||
config = default_config()
|
||||
config.enable_update_checks = False
|
||||
update_notifier = FakeUpdateGateway(update=None)
|
||||
|
|
@ -31,8 +37,55 @@ class SnapshotTestAppWithWhatsNew(BaseSnapshotTestApp):
|
|||
update_notifier=update_notifier,
|
||||
update_cache_repository=update_cache_repository,
|
||||
current_version="1.0.0",
|
||||
plan_offer_gateway=gateway,
|
||||
)
|
||||
|
||||
def on_unmount(self) -> None:
|
||||
if self._previous_api_key is None:
|
||||
os.environ.pop("MISTRAL_API_KEY", None)
|
||||
else:
|
||||
os.environ["MISTRAL_API_KEY"] = self._previous_api_key
|
||||
return None
|
||||
|
||||
|
||||
class SnapshotTestAppWithPlanUpgradeCTA(SnapshotTestAppWithWhatsNew):
|
||||
def __init__(self):
|
||||
plan_offer_gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=False,
|
||||
advertise_pro_plan=True,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
|
||||
super().__init__(gateway=plan_offer_gateway)
|
||||
|
||||
|
||||
class SnapshotTestAppWithSwitchKeyCTA(SnapshotTestAppWithWhatsNew):
|
||||
def __init__(self):
|
||||
plan_offer_gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=False,
|
||||
advertise_pro_plan=False,
|
||||
prompt_switching_to_pro_plan=True,
|
||||
)
|
||||
)
|
||||
|
||||
super().__init__(gateway=plan_offer_gateway)
|
||||
|
||||
|
||||
class SnapshotTestAppWithWhatsNewNoPlanCTA(SnapshotTestAppWithWhatsNew):
|
||||
def __init__(self):
|
||||
plan_offer_gateway = FakeWhoAmIGateway(
|
||||
WhoAmIResponse(
|
||||
is_pro_plan=True,
|
||||
advertise_pro_plan=False,
|
||||
prompt_switching_to_pro_plan=False,
|
||||
)
|
||||
)
|
||||
|
||||
super().__init__(gateway=plan_offer_gateway)
|
||||
|
||||
|
||||
def test_snapshot_shows_whats_new_message(
|
||||
snap_compare: SnapCompare, tmp_path: Path
|
||||
|
|
@ -50,3 +103,54 @@ def test_snapshot_shows_whats_new_message(
|
|||
terminal_size=(120, 36),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_shows_upgrade_message(
|
||||
snap_compare: SnapCompare, tmp_path: Path
|
||||
) -> None:
|
||||
whats_new_file = tmp_path / "whats_new.md"
|
||||
whats_new_file.write_text("# What's New\n\n- Feature 1\n- Feature 2\n- Feature 3")
|
||||
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.5)
|
||||
|
||||
with patch("vibe.cli.update_notifier.whats_new.VIBE_ROOT", tmp_path):
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_whats_new.py:SnapshotTestAppWithPlanUpgradeCTA",
|
||||
terminal_size=(120, 36),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_shows_switch_message(
|
||||
snap_compare: SnapCompare, tmp_path: Path
|
||||
) -> None:
|
||||
whats_new_file = tmp_path / "whats_new.md"
|
||||
whats_new_file.write_text("# What's New\n\n- Feature 1\n- Feature 2\n- Feature 3")
|
||||
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.5)
|
||||
|
||||
with patch("vibe.cli.update_notifier.whats_new.VIBE_ROOT", tmp_path):
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_whats_new.py:SnapshotTestAppWithSwitchKeyCTA",
|
||||
terminal_size=(120, 36),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
||||
|
||||
def test_snapshot_shows_no_plan_message(
|
||||
snap_compare: SnapCompare, tmp_path: Path
|
||||
) -> None:
|
||||
whats_new_file = tmp_path / "whats_new.md"
|
||||
whats_new_file.write_text("# What's New\n\n- Feature 1\n- Feature 2\n- Feature 3")
|
||||
|
||||
async def run_before(pilot: Pilot) -> None:
|
||||
await pilot.pause(0.5)
|
||||
|
||||
with patch("vibe.cli.update_notifier.whats_new.VIBE_ROOT", tmp_path):
|
||||
assert snap_compare(
|
||||
"test_ui_snapshot_whats_new.py:SnapshotTestAppWithWhatsNewNoPlanCTA",
|
||||
terminal_size=(120, 36),
|
||||
run_before=run_before,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ from typing import Any
|
|||
from acp import (
|
||||
Agent as AcpAgent,
|
||||
Client,
|
||||
CreateTerminalResponse,
|
||||
KillTerminalCommandResponse,
|
||||
ReadTextFileResponse,
|
||||
ReleaseTerminalResponse,
|
||||
RequestPermissionResponse,
|
||||
SessionNotification,
|
||||
TerminalHandle,
|
||||
TerminalOutputResponse,
|
||||
WaitForTerminalExitResponse,
|
||||
WriteTextFileResponse,
|
||||
|
|
@ -20,6 +20,7 @@ from acp.schema import (
|
|||
AgentPlanUpdate,
|
||||
AgentThoughtChunk,
|
||||
AvailableCommandsUpdate,
|
||||
ConfigOptionUpdate,
|
||||
CurrentModeUpdate,
|
||||
EnvVariable,
|
||||
PermissionOption,
|
||||
|
|
@ -27,6 +28,7 @@ from acp.schema import (
|
|||
ToolCallProgress,
|
||||
ToolCallStart,
|
||||
ToolCallUpdate,
|
||||
UsageUpdate,
|
||||
UserMessageChunk,
|
||||
)
|
||||
|
||||
|
|
@ -48,7 +50,9 @@ class FakeClient(Client):
|
|||
| AgentPlanUpdate
|
||||
| AvailableCommandsUpdate
|
||||
| CurrentModeUpdate
|
||||
| SessionInfoUpdate,
|
||||
| SessionInfoUpdate
|
||||
| ConfigOptionUpdate
|
||||
| UsageUpdate,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
self._session_updates.append(
|
||||
|
|
@ -88,7 +92,7 @@ class FakeClient(Client):
|
|||
env: list[EnvVariable] | None = None,
|
||||
output_byte_limit: int | None = None,
|
||||
**kwargs: Any,
|
||||
) -> TerminalHandle:
|
||||
) -> CreateTerminalResponse:
|
||||
raise NotImplementedError()
|
||||
|
||||
async def terminal_output(
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from tests.conftest import build_test_agent_loop, build_test_vibe_config
|
||||
from tests.mock.utils import mock_llm_chunk
|
||||
from tests.stubs.fake_backend import FakeBackend
|
||||
from vibe.core.agent_loop import AgentLoop
|
||||
from vibe.core.config import SessionLoggingConfig, VibeConfig
|
||||
from vibe.core.types import (
|
||||
AssistantEvent,
|
||||
CompactEndEvent,
|
||||
|
|
@ -27,10 +26,10 @@ async def test_auto_compact_triggers_and_batches_observer() -> None:
|
|||
[mock_llm_chunk(content="<summary>")],
|
||||
[mock_llm_chunk(content="<final>")],
|
||||
])
|
||||
cfg = VibeConfig(
|
||||
session_logging=SessionLoggingConfig(enabled=False), auto_compact_threshold=1
|
||||
cfg = build_test_vibe_config(auto_compact_threshold=1)
|
||||
agent = build_test_agent_loop(
|
||||
config=cfg, message_observer=observer, backend=backend
|
||||
)
|
||||
agent = AgentLoop(cfg, message_observer=observer, backend=backend)
|
||||
agent.stats.context_tokens = 2
|
||||
|
||||
events = [ev async for ev in agent.act("Hello")]
|
||||
|
|
|
|||