Replace the bulky hero installers with a compact platform switcher, wrapping command, and copy action

Hop-State: A_06FN6S8S3T184C0AS6MXQ08
Hop-Proposal: R_06FN6S5PSBAXTT4CDRPQS0R
Hop-Task: T_06FN6N560GHE00H1ACBKR8G
Hop-Attempt: AT_06FN6N560G6CPSCDTN8C48R
This commit is contained in:
Hop
2026-07-11 15:56:02 -07:00
parent fee4f6dd31
commit 0cb027737e
3 changed files with 202 additions and 74 deletions
@@ -305,6 +305,72 @@
load(); load();
} }
async function copyToClipboard(value) {
if (navigator.clipboard?.writeText) {
try {
await navigator.clipboard.writeText(value);
return true;
} catch {
// Fall back for browsers that block clipboard access outside a secure context.
}
}
const input = document.createElement('textarea');
input.value = value;
input.setAttribute('readonly', '');
input.style.position = 'fixed';
input.style.opacity = '0';
document.body.append(input);
input.select();
input.setSelectionRange(0, value.length);
const copied = document.execCommand('copy');
input.remove();
return copied;
}
function bindInstallationSwitchers() {
for (const switcher of document.querySelectorAll('.hop-agent-install-shell')) {
if (switcher.dataset.hopInstallBound === 'true') continue;
switcher.dataset.hopInstallBound = 'true';
const buttons = [...switcher.querySelectorAll('[data-hop-install-command]')];
const output = switcher.querySelector('[data-hop-install-command-output]');
const copyButton = switcher.querySelector('[data-hop-copy]');
const status = copyButton ? document.getElementById(copyButton.getAttribute('aria-describedby')) : null;
for (const button of buttons) {
button.addEventListener('click', () => {
for (const option of buttons) option.setAttribute('aria-pressed', String(option === button));
if (output) output.textContent = button.dataset.hopInstallCommand;
if (copyButton) copyButton.setAttribute('aria-label', `Copy ${button.dataset.hopInstallPlatform} installation command`);
if (status) status.textContent = '';
});
}
}
}
function bindInstallationCopyButtons() {
for (const button of document.querySelectorAll('[data-hop-copy]')) {
if (button.dataset.hopCopyBound === 'true') continue;
button.dataset.hopCopyBound = 'true';
button.addEventListener('click', async () => {
const command = button.closest('.hop-agent-install-command')?.querySelector('code')?.textContent?.trim();
const status = document.getElementById(button.getAttribute('aria-describedby'));
if (!command) return;
button.disabled = true;
button.textContent = 'Copying…';
const copied = await copyToClipboard(command);
button.disabled = false;
button.textContent = copied ? 'Copied' : 'Retry';
if (status) status.textContent = copied ? 'Command copied to clipboard.' : 'Could not copy automatically. Select the command and copy it.';
window.setTimeout(() => {
button.textContent = 'Copy';
if (status) status.textContent = '';
}, 2400);
});
}
}
function replaceBrandImages() { function replaceBrandImages() {
const upstreamBrandAsset = /\/img\/(?:avatar_default\.png|logo\.png|logo\.svg)(?:\?.*)?$/; const upstreamBrandAsset = /\/img\/(?:avatar_default\.png|logo\.png|logo\.svg)(?:\?.*)?$/;
for (const image of document.querySelectorAll('img[src]')) { for (const image of document.querySelectorAll('img[src]')) {
@@ -322,6 +388,8 @@
addPromptsNavigation(); addPromptsNavigation();
addWorkflowSummary(); addWorkflowSummary();
renderPromptPage(); renderPromptPage();
bindInstallationSwitchers();
bindInstallationCopyButtons();
const title = replacePhrases(document.title); const title = replacePhrases(document.title);
if (title !== document.title) document.title = title; if (title !== document.title) document.title = title;
document.documentElement.dataset.hopNative = 'true'; document.documentElement.dataset.hopNative = 'true';
+1 -1
View File
@@ -1,3 +1,3 @@
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/hop-home.css?v=4"> <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/hop-home.css?v=4">
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/hop-prompts.css?v=1"> <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/hop-prompts.css?v=1">
<script src="{{AssetUrlPrefix}}/js/hop-native.js?v=9"></script> <script src="{{AssetUrlPrefix}}/js/hop-native.js?v=10"></script>
+133 -73
View File
@@ -242,37 +242,121 @@
font-weight: 550; font-weight: 550;
} }
.hop-agent-install { .hop-agent-installs {
border-top: 1px solid var(--color-secondary);
margin-top: 1.5rem;
max-width: 100%;
padding-top: 1rem;
}
.hop-agent-install-heading {
align-items: baseline;
display: flex;
gap: 1rem;
justify-content: space-between;
}
.hop-agent-install-shell {
border: 1px solid var(--color-secondary);
border-radius: var(--border-radius);
margin-top: 0.7rem;
min-width: 0;
overflow: hidden;
}
.hop-agent-install-tabs {
align-items: center;
display: flex;
gap: 0.25rem;
padding: 0.25rem;
}
.hop-agent-install-tab {
background: transparent;
border: 0;
border-radius: calc(var(--border-radius) - 2px);
color: var(--color-text-light-2);
cursor: pointer;
font: inherit;
font-size: 0.72rem;
font-weight: 650;
line-height: 1;
padding: 0.55rem 0.7rem;
}
.hop-agent-install-tab[aria-pressed="true"] {
background: color-mix(in srgb, var(--color-primary) 18%, transparent);
color: var(--color-text);
}
.hop-agent-install-tab:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 1px;
}
.hop-agent-install-command {
align-items: center;
background: color-mix(in srgb, var(--color-secondary) 42%, transparent);
border-top: 1px solid var(--color-secondary); border-top: 1px solid var(--color-secondary);
display: grid; display: grid;
gap: clamp(3rem, 7vw, 6rem); gap: 0.75rem;
grid-template-columns: minmax(220px, 0.7fr) minmax(0, 1.3fr); grid-template-columns: minmax(0, 1fr) auto;
margin-top: clamp(3.5rem, 5vw, 5rem); min-width: 0;
padding-top: clamp(2.25rem, 3.5vw, 3.5rem); padding: 0.75rem 0.8rem;
} }
.hop-agent-install-copy h2 { .hop-agent-copy-button {
font-size: clamp(2.2rem, 4vw, 3.6rem); background: transparent;
border: 1px solid var(--color-secondary);
border-radius: var(--border-radius);
color: var(--color-text);
cursor: pointer;
font: inherit;
font-size: 0.78rem;
font-weight: 650; font-weight: 650;
letter-spacing: -0.045em; line-height: 1;
line-height: 1.04; padding: 0.5rem 0.65rem;
margin-top: 0.9rem;
} }
.hop-agent-install-copy > p:last-of-type { .hop-agent-copy-button:hover {
color: var(--color-text-light-2) !important; background: color-mix(in srgb, var(--color-primary) 16%, transparent);
font-size: 1rem; }
line-height: 1.6;
margin-top: 1.2rem !important; .hop-agent-copy-button:focus-visible {
max-width: 42ch; outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.hop-agent-install-command code {
color: var(--color-text);
display: block;
font-family: var(--fonts-monospace);
font-size: clamp(0.72rem, 1.12vw, 0.84rem);
line-height: 1.45;
min-width: 0;
overflow-wrap: anywhere;
white-space: pre-wrap;
word-break: break-word;
}
.hop-agent-copy-status {
color: var(--color-text-light-2);
display: block;
font-size: 0.78rem;
line-height: 1.3;
margin-top: 0.45rem;
}
.hop-agent-copy-status:empty {
display: none;
} }
.hop-agent-repo-link { .hop-agent-repo-link {
align-items: center; align-items: center;
display: inline-flex; display: inline-flex;
font-weight: 650; font-size: 0.84rem;
font-weight: 600;
gap: 0.45rem; gap: 0.45rem;
margin-top: 1.5rem;
} }
.hop-agent-repo-link::after { .hop-agent-repo-link::after {
@@ -280,29 +364,6 @@
font-size: 0.9em; font-size: 0.9em;
} }
.hop-agent-install-list {
border-top: 1px solid var(--color-secondary);
}
.hop-agent-install-command {
border-bottom: 1px solid var(--color-secondary);
padding: 1.45rem 0;
}
.hop-agent-install-command code {
background: color-mix(in srgb, var(--color-secondary) 42%, transparent);
border-radius: var(--border-radius);
color: var(--color-text);
display: block;
font-family: var(--fonts-monospace);
font-size: clamp(0.78rem, 1.35vw, 0.94rem);
line-height: 1.55;
margin-top: 0.85rem;
overflow-x: auto;
padding: 0.85rem 1rem;
white-space: pre;
}
.hop-agent-finish { .hop-agent-finish {
align-items: end; align-items: end;
border-top: 1px solid var(--color-secondary); border-top: 1px solid var(--color-secondary);
@@ -345,10 +406,6 @@
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.hop-agent-install {
grid-template-columns: 1fr;
}
.hop-agent-flow .hop-agent-section-heading { .hop-agent-flow .hop-agent-section-heading {
max-width: 620px; max-width: 620px;
} }
@@ -454,9 +511,14 @@
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.hop-agent-install-command code { .hop-agent-install-heading {
font-size: 0.76rem; align-items: center;
} }
.hop-agent-install-command {
align-items: start;
}
} }
</style> </style>
<main id="hop-main" tabindex="-1" role="main" aria-label="Hop, agent-first version control" class="page-content hop-agent-home"> <main id="hop-main" tabindex="-1" role="main" aria-label="Hop, agent-first version control" class="page-content hop-agent-home">
@@ -466,13 +528,30 @@
<h1 id="hop-agent-title"><span>Tell agents what to build.</span><span>Hop handles the Git.</span></h1> <h1 id="hop-agent-title"><span>Tell agents what to build.</span><span>Hop handles the Git.</span></h1>
<p class="hop-agent-lede">Run one coding agent or many. Hop captures the request, isolates every attempt, combines concurrent edits, and routes real conflicts through an agent reconciliation loop. Once checks pass, the agent lands the result automatically by default. You do not have to manage a branch or write a commit.</p> <p class="hop-agent-lede">Run one coding agent or many. Hop captures the request, isolates every attempt, combines concurrent edits, and routes real conflicts through an agent reconciliation loop. Once checks pass, the agent lands the result automatically by default. You do not have to manage a branch or write a commit.</p>
<div class="hop-agent-actions"> <div class="hop-agent-actions">
{{if .IsSigned}} {{if .IsSigned}}
<a class="ui primary button" href="{{AppSubUrl}}/repo/create">Create a repository</a> <a class="ui primary button" href="{{AppSubUrl}}/repo/create">Create a repository</a>
<a class="ui basic button" href="{{AppSubUrl}}/explore/repos">Explore repositories</a> <a class="ui basic button" href="{{AppSubUrl}}/explore/repos">Explore repositories</a>
{{else}} {{else}}
<a class="ui primary button" href="{{AppSubUrl}}/user/sign_up">Create a Hop account</a> <a class="ui primary button" href="{{AppSubUrl}}/user/sign_up">Create a Hop account</a>
<a class="ui basic button" href="#why-hop">Why use Hop?</a> <a class="ui basic button" href="#why-hop">Why use Hop?</a>
{{end}} {{end}}
</div>
<div class="hop-agent-installs" aria-label="Install Hop">
<div class="hop-agent-install-heading">
<p class="hop-agent-label">Install Hop</p>
<a class="hop-agent-repo-link" href="https://githop.xyz/GnosysLabs/Hop" target="_blank" rel="noreferrer">Repository</a>
</div>
<div class="hop-agent-install-shell">
<div class="hop-agent-install-tabs" role="group" aria-label="Installation platform">
<button class="hop-agent-install-tab" type="button" aria-pressed="true" data-hop-install-command="curl -fsSL https://githop.xyz/GnosysLabs/Hop/raw/branch/main/scripts/install.sh | sh" data-hop-install-platform="macOS and Linux">macOS + Linux</button>
<button class="hop-agent-install-tab" type="button" aria-pressed="false" data-hop-install-command="irm https://githop.xyz/GnosysLabs/Hop/raw/branch/main/scripts/install.ps1 | iex" data-hop-install-platform="Windows PowerShell">Windows</button>
</div>
<div class="hop-agent-install-command">
<code data-hop-install-command-output>curl -fsSL https://githop.xyz/GnosysLabs/Hop/raw/branch/main/scripts/install.sh | sh</code>
<button class="hop-agent-copy-button" type="button" data-hop-copy aria-label="Copy macOS and Linux installation command" aria-describedby="hop-install-status">Copy</button>
</div>
</div>
<span class="hop-agent-copy-status" id="hop-install-status" role="status" aria-live="polite"></span>
</div> </div>
<p class="hop-agent-note">Git stays underneath for compatibility. The mechanics disappear from your workflow.</p> <p class="hop-agent-note">Git stays underneath for compatibility. The mechanics disappear from your workflow.</p>
</div> </div>
@@ -534,25 +613,6 @@
</div> </div>
</section> </section>
<section class="hop-agent-install" id="install-hop" aria-labelledby="hop-agent-install-title">
<div class="hop-agent-install-copy">
<p class="hop-agent-label">Install Hop</p>
<h2 id="hop-agent-install-title">One install. No Git busywork.</h2>
<p>Install the Hop CLI, then tell your agents what to build. Hop handles isolated workspaces, checkpoints, reconciliation, and landing.</p>
<a class="hop-agent-repo-link" href="https://githop.xyz/GnosysLabs/Hop" target="_blank" rel="noreferrer">Official Hop repository</a>
</div>
<div class="hop-agent-install-list" aria-label="Hop installation commands">
<div class="hop-agent-install-command">
<p class="hop-agent-label">macOS and Linux</p>
<code>curl -fsSL https://githop.xyz/GnosysLabs/Hop/raw/branch/main/scripts/install.sh | sh</code>
</div>
<div class="hop-agent-install-command">
<p class="hop-agent-label">Windows PowerShell</p>
<code>irm https://githop.xyz/GnosysLabs/Hop/raw/branch/main/scripts/install.ps1 | iex</code>
</div>
</div>
</section>
<section class="hop-agent-finish" aria-labelledby="hop-agent-finish-title"> <section class="hop-agent-finish" aria-labelledby="hop-agent-finish-title">
<div> <div>
<h2 id="hop-agent-finish-title">Git compatibility without Git busywork.</h2> <h2 id="hop-agent-finish-title">Git compatibility without Git busywork.</h2>