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
18 changes: 17 additions & 1 deletion app/assets/stylesheets/components/_terminal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,25 @@

pre {
margin: 0;
overflow-x: auto;
padding: .875rem 1rem;

// Wrap, do not scroll.
//
// This block sits in a one-third column on /get-started and the ipctool
// URL is longer than the column is wide, so overflow-x: auto ended the
// visible line at "https://github.com/OpenI" and put the rest behind a
// horizontal scrollbar -- which most systems do not draw until you touch
// it, so there was nothing to suggest the command continued at all.
//
// The command being clipped is one a reader is about to paste into a root
// shell on their camera. Being able to read it to the end before running
// it is the whole point of showing it rather than just linking a script.
white-space: pre-wrap;
// Break inside the URL when there is nowhere else to break. `anywhere`
// rather than `break-all`, so ordinary prose still breaks at spaces and
// only an unbreakable token is split.
overflow-wrap: anywhere;

code {
color: inherit;
font-size: .875rem;
Expand Down
7 changes: 6 additions & 1 deletion app/javascript/src/copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ export default function initCopy() {
const source = document.querySelector(btn.dataset.copyTarget)
if (!source) return

navigator.clipboard.writeText(source.innerText.trim()).then(() => {
// textContent, not innerText. innerText is the *rendered* text, so with the
// terminal block now soft-wrapping a long URL there is no guarantee an
// engine will not fold those visual breaks into the string. textContent is
// the markup's own text: exactly the command, with only the newlines that
// were actually written.
navigator.clipboard.writeText(source.textContent.trim()).then(() => {
const icon = btn.querySelector('i') || btn
const original = icon.className
icon.className = 'bi bi-check-lg'
Expand Down
16 changes: 14 additions & 2 deletions app/views/pages/get_started.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
<div class="col-lg-4">
<h2 class="h5"><span class="text-data text-primary me-2">1</span><%= t('.step1_title') %></h2>
<p class="text-body-secondary"><%= t('.step1_text_html') %></p>
<%= render 'shared/terminal', id: 'ipctool-cmd', title: 'camera shell',
code: "curl -L -o /tmp/ipctool https://github.com/OpenIPC/ipctool/releases/download/latest/ipctool\nchmod +x /tmp/ipctool && /tmp/ipctool" %>
</div>
<div class="col-lg-4">
<h2 class="h5"><span class="text-data text-primary me-2">2</span><%= t('.step2_title') %></h2>
Expand All @@ -23,6 +21,20 @@
</div>
</div>

<%# The command for step 1, given the full width of the article rather than
the third of it the step sits in.

It was inside the step-1 column, where the ipctool URL is longer than
the column is wide: the visible line ended at "https://github.com/OpenI"
and the rest sat behind a horizontal scrollbar most systems do not draw.
The block wraps rather than scrolls now, so nothing is ever hidden -- but
at this width the command does not have to wrap at all on a desktop, and
two lines that are two commands read as two commands. %>
<div class="mb-5">
<%= render 'shared/terminal', id: 'ipctool-cmd', title: 'camera shell',
code: "curl -L -o /tmp/ipctool https://github.com/OpenIPC/ipctool/releases/download/latest/ipctool\nchmod +x /tmp/ipctool && /tmp/ipctool" %>
Comment thread
openipc-ai marked this conversation as resolved.
</div>

<%# Honesty box %>
<div class="alert alert-warning d-flex gap-3 mb-5">
<i class="bi bi-exclamation-triangle-fill fs-4"></i>
Expand Down
17 changes: 17 additions & 0 deletions test/controllers/relaunch_pages_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,23 @@ class RelaunchPagesTest < ActionDispatch::IntegrationTest
end
end

# /get-started shows a command to paste into a root shell on the camera. It
# has to reach the page whole -- the URL was being clipped by CSS, and the
# tempting fix is to shorten the command rather than let it wrap.
test 'the ipctool command reaches the page complete' do
get '/get-started'

block = css_select('#ipctool-cmd').first

assert_not_nil block, 'the terminal block is gone'
command = block.text
assert_includes command, 'https://github.com/OpenIPC/ipctool/releases/download/latest/ipctool'
assert_includes command, 'chmod +x /tmp/ipctool'
# The copy button copies this element, so it must name it correctly.
assert_not_empty css_select('[data-copy-target="#ipctool-cmd"]'),
'nothing on the page copies the command'
end

# The integrator wall is territory-specific: these companies serve Russia and
# were explicitly not to be shown to everyone.
test 'Russian integrators appear for ru and for nobody else' do
Expand Down
Loading