Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 63 additions & 4 deletions static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4826,6 +4826,42 @@ body.keyboard-open.notepad-focused .notepad-panel {
margin-bottom: 20px;
}

.quick-connect-details-card {
padding: 0;
overflow: clip;
}

.quick-connect-details-card .quick-connect-card-header {
margin-bottom: 0;
padding: 18px 20px;
}

.quick-connect-details-content {
padding: 18px 20px 20px;
border-top: 1px solid var(--border-color);
}

.quick-connect-auth-method {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(180px, 44%);
align-items: center;
gap: 18px;
margin-bottom: 24px;
}

.quick-connect-auth-method label {
margin-bottom: 0;
}

.quick-connect-core-fields {
display: grid;
gap: 14px;
}

.quick-connect-core-fields :is(.form-row, .form-group, .connection-profile-context) {
margin-bottom: 0;
}

.quick-connect-card-header.compact {
margin-bottom: 14px;
}
Expand Down Expand Up @@ -4861,10 +4897,6 @@ body.keyboard-open.notepad-focused .notepad-panel {
line-height: 1.45;
}

.quick-connect-details-card > .form-group:last-child {
margin-bottom: 0;
}

.connection-profile-context {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
Expand Down Expand Up @@ -4998,6 +5030,18 @@ body.keyboard-open.notepad-focused .notepad-panel {
margin-bottom: 0;
}

.connection-advanced-content .post-connect-label {
color: var(--text-primary);
font-size: 13px;
letter-spacing: 0.3px;
}

.connection-advanced-content .post-connect-preview-header {
color: var(--text-secondary);
font-size: 12px;
font-weight: 500;
}

@media (max-width: 900px) {
.quick-connect-grid {
grid-template-columns: minmax(0, 1fr);
Expand All @@ -5018,6 +5062,21 @@ body.keyboard-open.notepad-focused .notepad-panel {
padding: 16px;
}

.quick-connect-details-card {
padding: 0;
}

.quick-connect-details-card .quick-connect-card-header,
.quick-connect-details-content {
padding: 16px;
}

.quick-connect-auth-method {
grid-template-columns: minmax(0, 1fr);
gap: 8px;
margin-bottom: 20px;
}

:is(.connection-advanced-settings, .recent-connections-card) {
padding: 0;
}
Expand Down
4 changes: 2 additions & 2 deletions static/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const translations = {
'connection.recentConnections': 'Recent Connections',
'connection.recentConnectionsHint': 'Stored only in this browser for your account.',
'connection.noRecentConnections': 'Your recent connections will appear here.',
'connection.details': 'Connection details',
'connection.details': 'Connection Details',
'connection.detailsHint': 'Enter the destination and choose how to authenticate.',
'connection.savedContext': 'Saved connection',
'connection.reviewBeforeConnect': 'Review the settings and provide any required credentials.',
Expand Down Expand Up @@ -67,7 +67,7 @@ const translations = {
'connection.jumpHostHint': 'Manage jump hosts in the account menu.',
'connection.jumpHostPassword': 'Jump Host Password',
'connection.jumpHostPasswordHint': 'This bastion uses password auth — enter its password (never stored).',
'connection.advancedSettings': 'Advanced settings',
'connection.advancedSettings': 'Advanced Settings',
'connection.advancedSettingsHint': 'Jump host, post-connect actions, and persistence.',
'connection.commandSet': 'Commands after connecting (optional)',
'connection.commandSetHint': 'Runs on the remote host after a successful connection, not in WebSSH. Not run again when reconnecting to an existing tmux session.',
Expand Down
34 changes: 19 additions & 15 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

<link rel="stylesheet" href="{{ url_for('static', filename='vendor/material-icons/material-icons.css') }}">

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}?v=14">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}?v=15">
<link rel="stylesheet" href="{{ url_for('static', filename='css/sftp-file-manager.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/session-workspace.css') }}?v=4">
</head>
Expand Down Expand Up @@ -419,11 +419,24 @@ <h2 id="connectionModalTitle" data-i18n="connection.newSSHConnection">Quick Conn
<div class="quick-connect-card-header">
<span class="material-icons" aria-hidden="true">terminal</span>
<div>
<h3 id="connectionDetailsTitle" data-i18n="connection.details">Connection details</h3>
<h3 id="connectionDetailsTitle" data-i18n="connection.details">Connection Details</h3>
<p data-i18n="connection.detailsHint">Enter the destination and choose how to authenticate.</p>
</div>
</div>

<div class="quick-connect-details-content">
<div class="form-group quick-connect-auth-method">
<label for="authTypeSelect" data-i18n="connection.authMethod">Authentication Method</label>
<select id="authTypeSelect" class="form-control">
<option value="password" data-i18n="auth.password">Password</option>
<option value="key" data-i18n="connection.sshKey">SSH Key</option>
{% if tailscale_ssh_allowed %}
<option value="tailscale" data-i18n="connection.tailscaleSSH">Tailscale SSH</option>
{% endif %}
</select>
</div>

<div class="quick-connect-core-fields">
<div id="connectionProfileContext" class="connection-profile-context hidden" role="status">
<span class="material-icons" aria-hidden="true">bookmark</span>
<div>
Expand Down Expand Up @@ -459,17 +472,6 @@ <h3 id="connectionDetailsTitle" data-i18n="connection.details">Connection detail
<div class="field-hint" id="usernameHint"></div>
</div>

<div class="form-group">
<label for="authTypeSelect" data-i18n="connection.authMethod">Authentication Method</label>
<select id="authTypeSelect" class="form-control">
<option value="password" data-i18n="auth.password">Password</option>
<option value="key" data-i18n="connection.sshKey">SSH Key</option>
{% if tailscale_ssh_allowed %}
<option value="tailscale" data-i18n="connection.tailscaleSSH">Tailscale SSH</option>
{% endif %}
</select>
</div>

<div class="form-group" id="passwordGroup">
<label for="passwordInput" data-i18n="auth.password">Password *</label>
<div class="input-wrapper with-toggle">
Expand All @@ -487,6 +489,8 @@ <h3 id="connectionDetailsTitle" data-i18n="connection.details">Connection detail
<div class="field-hint" id="keyHint"></div>
</div>

</div>
</div>
</section>

<div class="quick-connect-secondary">
Expand All @@ -509,7 +513,7 @@ <h3 id="connectionDetailsTitle" data-i18n="connection.details">Connection detail
<summary>
<span class="material-icons" aria-hidden="true">tune</span>
<span>
<strong data-i18n="connection.advancedSettings">Advanced settings</strong>
<strong data-i18n="connection.advancedSettings">Advanced Settings</strong>
<small data-i18n="connection.advancedSettingsHint">Jump host, post-connect actions, and persistence.</small>
</span>
<span class="material-icons connection-advanced-chevron" aria-hidden="true">expand_more</span>
Expand Down Expand Up @@ -1225,7 +1229,7 @@ <h2 id="filePreviewTitle" data-i18n="preview.title">File Preview</h2>
<script src="{{ url_for('static', filename='vendor/highlight/highlight.min.js') }}"></script>
<script src="{{ url_for('static', filename='vendor/socketio/socket.io.min.js') }}"></script>

<script src="{{ url_for('static', filename='js/i18n.js') }}?v=10"></script>
<script src="{{ url_for('static', filename='js/i18n.js') }}?v=11"></script>

<script src="{{ url_for('static', filename='js/binary-transfer-client.js') }}"></script>
<script src="{{ url_for('static', filename='js/session-insights.js') }}?v=4"></script>
Expand Down
41 changes: 41 additions & 0 deletions tests/e2e/quick-connect-redesign.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,47 @@ test('presents a focused two-column quick connect without a saved-profile picker
expect(geometry.detailsRight).toBeLessThanOrEqual(geometry.recentLeft);
});

test('uses the requested connection details and advanced settings hierarchy', async ({ page }) => {
await page.locator('#newConnectionBtn').click();

const hierarchy = await page.locator('#connectionModal').evaluate(modal => {
const detailsContent = modal.querySelector('.quick-connect-details-content');
const authRow = modal.querySelector('.quick-connect-auth-method');
const authLabel = authRow?.querySelector('label');
const authSelect = authRow?.querySelector('select');
const host = modal.querySelector('#hostInput');
const jumpHostLabel = modal.querySelector('label[for="jumpHostSelect"]');
const runAfterLabel = modal.querySelector('.post-connect-label');
const previewLabel = modal.querySelector('.post-connect-preview-header');
const style = element => element ? getComputedStyle(element) : null;
const rect = element => element ? element.getBoundingClientRect() : null;

return {
detailsTitle: modal.querySelector('#connectionDetailsTitle')?.textContent.trim(),
advancedTitle: modal.querySelector('#connectionAdvancedSettings summary strong')?.textContent.trim(),
detailsBorder: style(detailsContent)?.borderTopWidth,
authLabelCenter: rect(authLabel) && (rect(authLabel).top + rect(authLabel).bottom) / 2,
authSelectCenter: rect(authSelect) && (rect(authSelect).top + rect(authSelect).bottom) / 2,
authBottom: rect(authRow)?.bottom,
hostTop: rect(host)?.top,
jumpHostFontSize: style(jumpHostLabel)?.fontSize,
runAfterFontSize: style(runAfterLabel)?.fontSize,
runAfterWeight: Number(style(runAfterLabel)?.fontWeight),
previewFontSize: style(previewLabel)?.fontSize,
previewWeight: Number(style(previewLabel)?.fontWeight),
};
});

expect(hierarchy.detailsTitle).toBe('Connection Details');
expect(hierarchy.advancedTitle).toBe('Advanced Settings');
expect(hierarchy.detailsBorder).toBe('1px');
expect(Math.abs(hierarchy.authLabelCenter - hierarchy.authSelectCenter)).toBeLessThan(2);
expect(hierarchy.authBottom).toBeLessThan(hierarchy.hostTop);
expect(hierarchy.runAfterFontSize).toBe(hierarchy.jumpHostFontSize);
expect(parseFloat(hierarchy.previewFontSize)).toBeLessThan(parseFloat(hierarchy.runAfterFontSize));
expect(hierarchy.previewWeight).toBeLessThan(hierarchy.runAfterWeight);
});

test('keeps modal actions fixed while expanded content scrolls inside', async ({ page }) => {
await page.setViewportSize({ width: 1100, height: 700 });
await page.evaluate(() => {
Expand Down
4 changes: 2 additions & 2 deletions tests/test_key_management_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ def test_key_replacement_event_updates_ui_and_asset_version():
assert "socket.on('key_replaced'" in APP
assert 'ProfileManager.upsertKeySummary(data.key)' in APP
assert "filename='js/profile-manager.js') }}?v=11" in TEMPLATE
assert "filename='js/i18n.js') }}?v=10" in TEMPLATE
assert "filename='js/i18n.js') }}?v=11" in TEMPLATE
assert "filename='js/app.js') }}?v=12" in TEMPLATE
assert "filename='css/style.css') }}?v=14" in TEMPLATE
assert "filename='css/style.css') }}?v=15" in TEMPLATE


def test_socket_events_refresh_key_ui_without_resetting_profile_editor():
Expand Down
6 changes: 3 additions & 3 deletions tests/test_profile_launcher_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ def test_template_has_one_empty_pane_renderer_and_loads_launcher_utility_first()
def test_merged_profile_frontend_assets_have_distinct_cache_versions():
template = read('templates/index.html')
expected_versions = {
"filename='css/style.css'": '?v=14',
"filename='js/i18n.js'": '?v=10',
"filename='css/style.css'": '?v=15',
"filename='js/i18n.js'": '?v=11',
"filename='js/command-workspace.js'": '?v=2',
"filename='js/command-palette-utils.js'": '?v=1',
"filename='js/profile-launcher-utils.js'": '?v=5',
Expand Down Expand Up @@ -131,7 +131,7 @@ def test_mobile_launcher_stacks_status_below_profile_details():
def test_profile_launcher_stylesheet_uses_current_cache_version():
template = read('templates/index.html')

assert "filename='css/style.css') }}?v=14" in template
assert "filename='css/style.css') }}?v=15" in template


def test_active_session_command_launcher_is_loaded_after_command_data_managers():
Expand Down
Loading