Download index.html from nikodembartnik/lerobot-command-builder: direct link, hf CLI and curl.
- Browser
- Download file 26.9 kB
-
https://huggingface.co/spaces/nikodembartnik/lerobot-command-builder/resolve/main/index.html
- Command line
-
hf download hf://spaces/nikodembartnik/lerobot-command-builder/index.html
-
curl -L -o index.html https://huggingface.co/spaces/nikodembartnik/lerobot-command-builder/resolve/main/index.html
26.9 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>LeRobot Command Builder</title> | |
| <meta | |
| name="description" | |
| content="Fill in your robot, ports and dataset once. Copy the exact LeRobot commands for calibration, teleoperation, recording, training and rollout." | |
| /> | |
| <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='.9em' font-size='90'%3E%F0%9F%A4%96%3C/text%3E%3C/svg%3E" /> | |
| <link rel="stylesheet" href="style.css" /> | |
| </head> | |
| <body> | |
| <a class="skip-link" href="#steps">Skip to commands</a> | |
| <header class="topbar"> | |
| <div class="topbar__brand"> | |
| <img class="logo" src="logo.png" alt="LeRobot" width="130" height="42" /> | |
| <div> | |
| <h1>Command Builder</h1> | |
| <p class="topbar__sub">Set your hardware once β copy the right command at every step.</p> | |
| </div> | |
| </div> | |
| <div class="topbar__actions"> | |
| <span class="saved-pill" id="savedPill" role="status" aria-live="polite">Saved locally</span> | |
| <button class="btn btn--ghost" id="themeToggle" type="button" title="Toggle light / dark"> | |
| <span id="themeIcon" aria-hidden="true">β</span><span class="sr-only">Toggle theme</span> | |
| </button> | |
| </div> | |
| </header> | |
| <div class="layout"> | |
| <!-- βββββββββββββββββββββββββ SIDEBAR βββββββββββββββββββββββββ --> | |
| <aside class="sidebar" aria-label="Configuration"> | |
| <section class="panel panel--profiles"> | |
| <h2 class="panel__title">Profile</h2> | |
| <p class="panel__hint">One profile per robot setup. Everything below is remembered in this browser.</p> | |
| <select id="profileSelect" aria-label="Active profile"></select> | |
| <label class="field field--tight"> | |
| <span class="field__label">Name</span> | |
| <input type="text" id="profileName" placeholder="SO-101 bench" autocomplete="off" /> | |
| </label> | |
| <div class="btn-row"> | |
| <button class="btn btn--sm" id="profileNew" type="button">New</button> | |
| <button class="btn btn--sm" id="profileDuplicate" type="button">Duplicate</button> | |
| <button class="btn btn--sm btn--danger" id="profileDelete" type="button">Delete</button> | |
| </div> | |
| <div class="btn-row btn-row--tight"> | |
| <button class="btn btn--sm btn--ghost" id="profileExport" type="button">Export JSON</button> | |
| <button class="btn btn--sm btn--ghost" id="profileImport" type="button">Import JSON</button> | |
| <button class="btn btn--sm btn--ghost" id="profileShare" type="button">Copy share link</button> | |
| </div> | |
| <input type="file" id="profileFile" accept=".json,application/json" hidden /> | |
| </section> | |
| <section class="panel"> | |
| <h2 class="panel__title">You</h2> | |
| <label class="field"> | |
| <span class="field__label">Hugging Face username</span> | |
| <input type="text" id="hfUser" placeholder="your-username" autocomplete="off" spellcheck="false" /> | |
| <span class="field__hint">Used as the <code><user>/<name></code> prefix for datasets and policies.</span> | |
| </label> | |
| </section> | |
| <section class="panel"> | |
| <h2 class="panel__title">Robot (follower)</h2> | |
| <label class="field"> | |
| <span class="field__label">Type</span> | |
| <select id="robotType"></select> | |
| </label> | |
| <p class="type-note" id="robotNote"></p> | |
| <label class="field"> | |
| <span class="field__label">Calibration ID</span> | |
| <input type="text" id="robotId" placeholder="my_follower" autocomplete="off" spellcheck="false" /> | |
| <span class="field__hint">A name you pick. Reuse it in every command so calibration is found.</span> | |
| </label> | |
| <div id="robotConnFields"></div> | |
| </section> | |
| <section class="panel"> | |
| <h2 class="panel__title">Teleoperator (leader)</h2> | |
| <label class="field"> | |
| <span class="field__label">Type</span> | |
| <select id="teleopType"></select> | |
| </label> | |
| <label class="field"> | |
| <span class="field__label">Calibration ID</span> | |
| <input type="text" id="teleopId" placeholder="my_leader" autocomplete="off" spellcheck="false" /> | |
| </label> | |
| <div id="teleopConnFields"></div> | |
| </section> | |
| <section class="panel"> | |
| <div class="panel__head"> | |
| <h2 class="panel__title">Cameras</h2> | |
| <button class="btn btn--sm" id="cameraAdd" type="button">+ Add</button> | |
| </div> | |
| <p class="panel__hint"> | |
| Camera keys become observation keys in your dataset β keep them stable between recording and rollout. | |
| </p> | |
| <div id="cameraList" class="camera-list"></div> | |
| </section> | |
| <section class="panel"> | |
| <h2 class="panel__title">Command style</h2> | |
| <label class="check"> | |
| <input type="checkbox" id="multiline" /> | |
| <span>Multi-line (<code>\</code> continuations)</span> | |
| </label> | |
| <label class="check"> | |
| <input type="checkbox" id="useHfVar" /> | |
| <span>Use <code>$HF_USER</code> instead of the literal name</span> | |
| </label> | |
| <label class="check"> | |
| <input type="checkbox" id="displayData" /> | |
| <span>Add <code>--display_data=true</code> (Rerun viewer)</span> | |
| </label> | |
| </section> | |
| </aside> | |
| <!-- βββββββββββββββββββββββββ STEPS βββββββββββββββββββββββββ --> | |
| <main class="steps" id="steps"> | |
| <nav class="stepnav" aria-label="Steps"> | |
| <a href="#step-1"><b>1</b> Calibrate</a> | |
| <a href="#step-2"><b>2</b> Teleoperate</a> | |
| <a href="#step-3"><b>3</b> Record</a> | |
| <a href="#step-4"><b>4</b> Train</a> | |
| <a href="#step-5"><b>5</b> Rollout</a> | |
| </nav> | |
| <!-- Prerequisites: installing LeRobot and finding serial ports happen | |
| before this tool is useful, so they are links rather than a step. --> | |
| <section class="prereq"> | |
| <p class="prereq__lead"> | |
| <strong>Before step 1</strong> β install LeRobot, assemble the arms and find your serial ports: | |
| </p> | |
| <p class="prereq__links"> | |
| <a id="docInstall" href="https://huggingface.co/docs/lerobot/installation" target="_blank" rel="noopener">Installation β</a> | |
| <a id="docHardware" href="https://huggingface.co/docs/lerobot/hardware_guide" target="_blank" rel="noopener">Hardware guide β</a> | |
| <a id="docRobot" href="https://huggingface.co/docs/lerobot/so101" target="_blank" rel="noopener">Robot setup β</a> | |
| <a id="docCheatsheet" href="https://huggingface.co/docs/lerobot/cheat-sheet" target="_blank" rel="noopener">CLI cheat-sheet β</a> | |
| </p> | |
| <p class="prereq__extra" id="prereqExtra"></p> | |
| </section> | |
| <div class="warnings" id="warnings" hidden></div> | |
| <!-- STEP 1 --> | |
| <section class="step" id="step-1"> | |
| <header class="step__head"> | |
| <span class="step__num">1</span> | |
| <div> | |
| <h2 class="step__title">Calibrate</h2> | |
| <p class="step__sub">Teaches LeRobot each joint's range. Do the follower and the leader separately.</p> | |
| </div> | |
| </header> | |
| <div class="step__body"> | |
| <div class="step__main"> | |
| <div class="cmd" data-cmd="calibrate_robot"></div> | |
| <div class="cmd" data-cmd="calibrate_teleop"></div> | |
| </div> | |
| <aside class="step__aside"> | |
| <h3>What to do physically</h3> | |
| <ol> | |
| <li>Move every joint to its <strong>middle</strong> position, then press Enter.</li> | |
| <li>Sweep each joint slowly through its <strong>full</strong> range, one at a time.</li> | |
| <li>Press Enter to save.</li> | |
| </ol> | |
| <p> | |
| Calibration is stored per <code>id</code>. If you change the <code>id</code> later, LeRobot will ask you | |
| to calibrate again β so pick a name and keep it. | |
| </p> | |
| <h3>When to redo it</h3> | |
| <ul> | |
| <li>You reassembled or reprinted a joint.</li> | |
| <li>You swapped a motor.</li> | |
| <li>Teleoperation feels offset even though the ports are right.</li> | |
| </ul> | |
| <h3>Read more</h3> | |
| <ul class="doclinks"> | |
| <li><a id="docRobotAside" href="https://huggingface.co/docs/lerobot/so101" target="_blank" rel="noopener">Your robot's setup guide β</a></li> | |
| <li><a href="https://huggingface.co/docs/lerobot/hardware_guide" target="_blank" rel="noopener">Hardware guide β</a></li> | |
| </ul> | |
| </aside> | |
| </div> | |
| </section> | |
| <!-- STEP 2 --> | |
| <section class="step" id="step-2"> | |
| <header class="step__head"> | |
| <span class="step__num">2</span> | |
| <div> | |
| <h2 class="step__title">Teleoperate</h2> | |
| <p class="step__sub">No recording. Prove the whole chain works before you spend an hour on data.</p> | |
| </div> | |
| </header> | |
| <div class="step__body"> | |
| <div class="step__main"> | |
| <div class="inline-fields"> | |
| <label class="field field--inline"> | |
| <span class="field__label">Control loop FPS</span> | |
| <input type="number" id="teleopFps" min="1" max="500" /> | |
| </label> | |
| </div> | |
| <div class="cmd" data-cmd="teleoperate"></div> | |
| </div> | |
| <aside class="step__aside"> | |
| <h3>Checklist before you record</h3> | |
| <ul> | |
| <li>Follower tracks the leader smoothly, no jumps or dead joints.</li> | |
| <li>Gripper opens and closes over its full range.</li> | |
| <li> | |
| Every camera shows up in the Rerun window, in focus, with the whole workspace visible. | |
| </li> | |
| <li> | |
| <strong>Ask yourself: could I do this task from the camera views alone?</strong> If not, move the | |
| cameras now β not after 50 episodes. | |
| </li> | |
| </ul> | |
| <p> | |
| Do 5β10 practice runs without recording. Decide on one repeatable strategy. Hesitant demos teach the | |
| model hesitation. | |
| </p> | |
| </aside> | |
| </div> | |
| </section> | |
| <!-- STEP 3 --> | |
| <section class="step" id="step-3"> | |
| <header class="step__head"> | |
| <span class="step__num">3</span> | |
| <div> | |
| <h2 class="step__title">Record a dataset</h2> | |
| <p class="step__sub">Arrow keys drive the session: β next, β redo, ESC finish & upload.</p> | |
| </div> | |
| </header> | |
| <div class="step__body"> | |
| <div class="step__main"> | |
| <div class="inline-fields"> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Dataset name</span> | |
| <div class="repo-input"> | |
| <span class="repo-input__prefix" id="datasetPrefix">user/</span> | |
| <input type="text" id="datasetName" placeholder="pick_place_lego" autocomplete="off" spellcheck="false" /> | |
| </div> | |
| </label> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Task description (one sentence)</span> | |
| <input type="text" id="taskDesc" placeholder="Pick the Lego block and drop it in the box on the right." /> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Episodes</span> | |
| <input type="number" id="numEpisodes" min="1" /> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Episode seconds</span> | |
| <input type="number" id="episodeTime" min="1" /> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Reset seconds</span> | |
| <input type="number" id="resetTime" min="0" /> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Dataset FPS</span> | |
| <input type="number" id="datasetFps" min="1" max="120" /> | |
| </label> | |
| </div> | |
| <div class="check-row"> | |
| <label class="check"> | |
| <input type="checkbox" id="pushToHub" /> | |
| <span>Push to the Hub when finished</span> | |
| </label> | |
| <label class="check"> | |
| <input type="checkbox" id="privateDataset" /> | |
| <span>Private dataset</span> | |
| </label> | |
| <label class="check"> | |
| <input type="checkbox" id="resumeRecording" /> | |
| <span>Resume an existing dataset</span> | |
| </label> | |
| </div> | |
| <div class="cmd" data-cmd="record"></div> | |
| <h3 class="sub-head">Then check your data β before training, always</h3> | |
| <div class="link-row"> | |
| <a class="btn btn--accent" id="vizLink" href="https://huggingface.co/spaces/lerobot/visualize_dataset" target="_blank" rel="noopener"> | |
| Open dataset viewer β | |
| </a> | |
| <span class="link-row__note" id="vizNote">Opens the Hub viewer for this dataset.</span> | |
| </div> | |
| <div class="cmd" data-cmd="viz"></div> | |
| <div class="cmd" data-cmd="replay"></div> | |
| </div> | |
| <aside class="step__aside"> | |
| <h3>The golden rule</h3> | |
| <p> | |
| Your first 50 episodes should be a <strong>constrained</strong> version of the task: one object, roughly | |
| fixed position, fixed cameras, one operator. Train a quick ACT model, see what fails, <em>then</em> widen. | |
| </p> | |
| <h3>Data quality beats model choice</h3> | |
| <ul> | |
| <li>Same grasp, same approach vector, same timing every episode.</li> | |
| <li>Deliberate and clean beats fast and sloppy. Speed comes last.</li> | |
| <li>Diffuse, consistent lighting matters more than resolution.</li> | |
| <li>Bolt down the rig and the cameras before recording anything.</li> | |
| </ul> | |
| <h3>What to look for in the viewer</h3> | |
| <ul> | |
| <li>Dropped frames or FPS dips.</li> | |
| <li>Motion blur or an out-of-frame gripper.</li> | |
| <li>Episodes where the task actually failed β delete them.</li> | |
| </ul> | |
| <h3>Read more</h3> | |
| <ul class="doclinks"> | |
| <li><a href="https://huggingface.co/docs/lerobot/il_robots" target="_blank" rel="noopener">Recording a dataset β</a></li> | |
| <li><a href="https://huggingface.co/docs/lerobot/cameras" target="_blank" rel="noopener">Camera setup β</a></li> | |
| </ul> | |
| </aside> | |
| </div> | |
| </section> | |
| <!-- STEP 4 --> | |
| <section class="step" id="step-4"> | |
| <header class="step__head"> | |
| <span class="step__num">4</span> | |
| <div> | |
| <h2 class="step__title">Train</h2> | |
| <p class="step__sub">Pick a policy, point it at a dataset, decide where the weights land.</p> | |
| </div> | |
| </header> | |
| <div class="step__body"> | |
| <div class="step__main"> | |
| <div class="inline-fields"> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Train on dataset</span> | |
| <div class="repo-input"> | |
| <button class="btn btn--sm btn--fill" id="useRecordedDataset" type="button"> | |
| β Use name from step 3 | |
| </button> | |
| <input type="text" id="trainDataset" placeholder="user/pick_place_lego" autocomplete="off" spellcheck="false" /> | |
| </div> | |
| <span class="field__hint"> | |
| Click to pull in the dataset you just recorded, or type any LeRobot-format Hub dataset β it does | |
| not have to be your own. | |
| </span> | |
| </label> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Policy</span> | |
| <select id="policyType"></select> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Device</span> | |
| <select id="device"></select> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Batch size</span> | |
| <input type="number" id="batchSize" min="1" /> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Steps</span> | |
| <input type="number" id="steps" min="1" step="1000" /> | |
| </label> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Output directory (local checkpoints)</span> | |
| <input type="text" id="outputDir" placeholder="outputs/train/act_pick_place_lego" autocomplete="off" spellcheck="false" /> | |
| </label> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Push trained policy to</span> | |
| <input type="text" id="policyRepo" placeholder="user/act_pick_place_lego" autocomplete="off" spellcheck="false" /> | |
| <span class="field__hint">Leave empty to keep the policy local only.</span> | |
| </label> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Run on Hugging Face Jobs (optional)</span> | |
| <input type="text" id="jobTarget" placeholder="a10g-small" autocomplete="off" spellcheck="false" /> | |
| <span class="field__hint">No local GPU? Put a flavor here. List them with <code>hf jobs hardware</code>.</span> | |
| </label> | |
| </div> | |
| <div class="check-row"> | |
| <label class="check"> | |
| <input type="checkbox" id="wandb" /> | |
| <span>Log to Weights & Biases</span> | |
| </label> | |
| <label class="check"> | |
| <input type="checkbox" id="resumeTraining" /> | |
| <span>Resume from last checkpoint</span> | |
| </label> | |
| </div> | |
| <div class="cmd" data-cmd="train"></div> | |
| <div class="cmd" data-cmd="train_resume"></div> | |
| </div> | |
| <aside class="step__aside"> | |
| <h3 id="policyNoteHead">Policy notes</h3> | |
| <p id="policyNote"></p> | |
| <h3>How long</h3> | |
| <ul> | |
| <li><strong>ACT, ~50 episodes:</strong> 50kβ100k steps is a reasonable first run.</li> | |
| <li>Watch the loss curve, but judge on the <em>real robot</em> β loss is a weak proxy for success rate.</li> | |
| <li>Out of VRAM? Halve the batch size before you touch anything else.</li> | |
| </ul> | |
| <h3>Checkpoints</h3> | |
| <p> | |
| Checkpoints land in <code id="ckptPath">outputs/train/<job>/checkpoints/</code>. The one you want at | |
| rollout time is <code>checkpoints/last/pretrained_model</code>. | |
| </p> | |
| <h3>Read more</h3> | |
| <ul class="doclinks"> | |
| <li><a id="docPolicy" href="https://huggingface.co/docs/lerobot/act" target="_blank" rel="noopener">This policy's guide β</a></li> | |
| <li><a href="https://huggingface.co/docs/lerobot/il_robots" target="_blank" rel="noopener">Imitation learning walkthrough β</a></li> | |
| <li><a href="https://huggingface.co/docs/lerobot/hardware_guide" target="_blank" rel="noopener">Hardware guide β</a></li> | |
| <li><a href="https://huggingface.co/docs/lerobot/multi_gpu_training" target="_blank" rel="noopener">Multi-GPU training β</a></li> | |
| <li><a href="https://huggingface.co/docs/hub/jobs" target="_blank" rel="noopener">Hugging Face Jobs β</a></li> | |
| </ul> | |
| </aside> | |
| </div> | |
| </section> | |
| <!-- STEP 5 --> | |
| <section class="step" id="step-5"> | |
| <header class="step__head"> | |
| <span class="step__num">5</span> | |
| <div> | |
| <h2 class="step__title">Rollout</h2> | |
| <p class="step__sub">Run the policy on the real robot β and measure it honestly.</p> | |
| </div> | |
| </header> | |
| <div class="step__body"> | |
| <div class="step__main"> | |
| <div class="inline-fields"> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Policy path or Hub repo</span> | |
| <div class="repo-input"> | |
| <button class="btn btn--sm btn--fill" id="useTrainedPolicy" type="button"> | |
| β Use policy from step 4 | |
| </button> | |
| <input type="text" id="policyPath" placeholder="outputs/train/act_pick_place_lego/checkpoints/last/pretrained_model" autocomplete="off" spellcheck="false" /> | |
| </div> | |
| <span class="field__hint"> | |
| Click to fill in where step 4 saved your policy, or paste a local checkpoint path or Hub repo id. | |
| </span> | |
| </label> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Strategy</span> | |
| <select id="rolloutStrategy"></select> | |
| </label> | |
| <label class="field field--inline field--wide"> | |
| <span class="field__label">Inference backend</span> | |
| <select id="inferenceType"></select> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Duration (s)</span> | |
| <input type="number" id="rolloutDuration" min="0" /> | |
| <span class="field__hint">0 = run until you stop it.</span> | |
| </label> | |
| <label class="field field--inline"> | |
| <span class="field__label">Rollout FPS</span> | |
| <input type="number" id="rolloutFps" min="1" max="500" /> | |
| </label> | |
| <label class="field field--inline field--wide" id="rolloutDatasetField"> | |
| <span class="field__label">Record rollouts into</span> | |
| <div class="repo-input"> | |
| <span class="repo-input__prefix" id="rolloutPrefix">user/</span> | |
| <input type="text" id="rolloutDataset" placeholder="eval_pick_place_lego" autocomplete="off" spellcheck="false" /> | |
| </div> | |
| </label> | |
| </div> | |
| <div class="check-row"> | |
| <label class="check"> | |
| <input type="checkbox" id="interactive" /> | |
| <span>Interactive mode (<code>/start</code>, <code>/subtask</code>, <code>/stop</code> from stdin)</span> | |
| </label> | |
| </div> | |
| <div class="cmd" data-cmd="rollout"></div> | |
| </div> | |
| <aside class="step__aside"> | |
| <h3 id="strategyNoteHead">Strategy</h3> | |
| <p id="strategyNote"></p> | |
| <h3 id="inferenceNoteHead">Inference</h3> | |
| <p id="inferenceNote"></p> | |
| <h3>Evaluate like you mean it</h3> | |
| <ul> | |
| <li> | |
| Run ~10 attempts under the <em>same</em> conditions as training and count successes. That number, not | |
| the loss curve, is your result. | |
| </li> | |
| <li>Compare against a teleoperated baseline β if you can't do it by hand reliably, the policy won't.</li> | |
| <li> | |
| Failures cluster. Watch where: wrong approach angle, grasp slips, object outside the training | |
| distribution. Each one points at different data to collect next. | |
| </li> | |
| </ul> | |
| <p> | |
| Want those attempts saved? Switch the strategy to <code>episodic</code> or <code>sentry</code> β they | |
| record while the policy runs. Feed failures back with <code>dagger</code>: you take over when it goes | |
| wrong, and only your corrections get recorded. | |
| </p> | |
| <h3>Read more</h3> | |
| <ul class="doclinks"> | |
| <li><a href="https://huggingface.co/docs/lerobot/inference" target="_blank" rel="noopener">Running inference β</a></li> | |
| <li><a href="https://huggingface.co/docs/lerobot/rtc" target="_blank" rel="noopener">Real-Time Chunking β</a></li> | |
| <li><a href="https://huggingface.co/docs/lerobot/hil_data_collection" target="_blank" rel="noopener">Human-in-the-loop data β</a></li> | |
| </ul> | |
| </aside> | |
| </div> | |
| </section> | |
| <footer class="foot"> | |
| <p> | |
| Built for <a href="https://github.com/huggingface/lerobot" target="_blank" rel="noopener">huggingface/lerobot</a>. | |
| Commands mirror the current CLI β check | |
| <a href="https://huggingface.co/docs/lerobot" target="_blank" rel="noopener">the docs</a> if a flag was renamed. | |
| </p> | |
| <p class="foot__privacy"> | |
| Everything you type stays in this browser's <code>localStorage</code>. Nothing is uploaded, and there is no | |
| backend to send it to. | |
| </p> | |
| </footer> | |
| </main> | |
| </div> | |
| <div class="toast" id="toast" role="status" aria-live="polite" hidden></div> | |
| <script type="module" src="app.js"></script> | |
| </body> | |
| </html> | |