File size: 7,238 Bytes
49ef41b
f1b492f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49ef41b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTML Studio β†’ harvis.dev</title>
<meta name="description" content="Generate a website with any free model on the Hugging Face router, then publish it live to harvis.dev in one click.">
<link rel="stylesheet" href="style.css">
</head>
<body>

<header class="topbar">
  <div class="brand">
    <span class="brand-mark">πŸ› οΈ</span>
    <h1>HTML&nbsp;Studio</h1>
    <span class="brand-arrow">β†’</span>
    <a class="brand-dest" href="https://harvis.dev" target="_blank" rel="noopener">harvis.dev</a>
  </div>

  <div class="auth" id="auth">
    <button id="signin" class="btn btn-hf" type="button">
      <svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><circle cx="12" cy="12" r="10" fill="#FFD21E"/><path d="M8.2 10.4a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm7.6 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2ZM12 18a5 5 0 0 1-5-4.4h10A5 5 0 0 1 12 18Z" fill="#3A2C0A"/></svg>
      Sign in with Hugging&nbsp;Face
    </button>
    <button id="tokentoggle" class="btn btn-ghost" type="button" title="Paste an access token instead">Use a token</button>
    <div id="whoami" class="whoami" hidden>
      <img id="avatar" alt="" width="24" height="24">
      <span id="username"></span>
      <button id="signout" class="btn btn-ghost btn-sm" type="button">Sign out</button>
    </div>
  </div>
</header>

<div id="tokenrow" class="tokenrow" hidden>
  <label for="token">Access token</label>
  <input id="token" type="password" autocomplete="off" spellcheck="false"
         placeholder="hf_… β€” from huggingface.co/settings/tokens">
  <button id="tokensave" class="btn btn-primary btn-sm" type="button">Save</button>
  <span class="hint">Stored in this browser only, never sent anywhere but the HF router.</span>
</div>

<main class="layout">

  <!-- ── control rail ───────────────────────────────────────────── -->
  <aside class="rail">
    <div class="field">
      <label for="model">Model</label>
      <div class="selectwrap">
        <select id="model"><option value="">Loading models…</option></select>
      </div>
      <p class="hint" id="modelhint">Every open-weights chat model on the HF Inference&nbsp;Providers router.</p>
    </div>

    <div class="field field-grow">
      <label for="prompt">Describe the site</label>
      <textarea id="prompt" spellcheck="false"
        placeholder="A dark landing page for a coffee subscription called Ember. Hero with a tagline, three plan cards with prices, an FAQ accordion, and a footer. Warm oranges, generous whitespace, subtle scroll animations."></textarea>
    </div>

    <details class="advanced">
      <summary>Advanced</summary>
      <div class="field">
        <label for="system">System prompt</label>
        <textarea id="system" rows="5" spellcheck="false"></textarea>
      </div>
      <div class="field-row">
        <div class="field">
          <label for="temp">Temperature <span id="tempval" class="mono">0.7</span></label>
          <input id="temp" type="range" min="0" max="1.5" step="0.05" value="0.7">
        </div>
        <div class="field">
          <label for="maxtok">Max tokens</label>
          <input id="maxtok" type="number" min="1000" max="128000" step="1000" value="16000">
        </div>
      </div>
    </details>

    <div class="actions">
      <button id="generate" class="btn btn-primary btn-lg" type="button">Generate</button>
      <button id="stop" class="btn btn-ghost btn-lg" type="button" hidden>Stop</button>
    </div>

    <div class="examples">
      <span class="examples-label">Try</span>
      <button class="chip" type="button" data-example="A one-page portfolio for a landscape photographer named Ilse Moreau. Full-bleed hero image from picsum.photos, a masonry gallery of 9 photos with lightbox on click, a short about section, and a contact form. Minimal, lots of white, thin serif headings.">Photo portfolio</button>
      <button class="chip" type="button" data-example="A playable Snake game. Canvas-based, arrow keys and swipe support, score and high score persisted, a start overlay, and a neon-on-black aesthetic with a subtle CRT scanline effect.">Snake game</button>
      <button class="chip" type="button" data-example="A pricing page for a developer tool called Relay. Three tiers with a monthly/annual toggle that actually recalculates prices, a feature comparison table, and a FAQ. Clean, modern SaaS look with a violet accent.">SaaS pricing</button>
      <button class="chip" type="button" data-example="An interactive dashboard showing fake sales data: four KPI tiles, a line chart of revenue over 12 months, and a bar chart by region. Draw the charts with inline SVG, no external libraries. Light theme, accessible colours.">Dashboard</button>
    </div>
  </aside>

  <!-- ── output ─────────────────────────────────────────────────── -->
  <section class="output">
    <div class="tabs" role="tablist">
      <button class="tab is-active" id="tab-preview" role="tab" aria-selected="true" type="button">Preview</button>
      <button class="tab" id="tab-code" role="tab" aria-selected="false" type="button">Code <span id="bytes" class="badge"></span></button>
      <div class="tabs-spacer"></div>
      <span id="status" class="status"></span>
      <button id="reload" class="btn btn-ghost btn-sm" type="button" title="Re-render the preview">Re-render</button>
      <button id="download" class="btn btn-ghost btn-sm" type="button">Download</button>
    </div>

    <div class="pane pane-preview" id="pane-preview">
      <div class="empty" id="empty">
        <div class="empty-art">&lt;/&gt;</div>
        <p>Your generated site renders here.</p>
        <p class="hint">Pick a model, describe what you want, hit&nbsp;Generate.</p>
      </div>
      <iframe id="preview" title="Live preview of the generated site"
              sandbox="allow-scripts allow-modals allow-popups allow-forms allow-pointer-lock allow-downloads"
              hidden></iframe>
    </div>

    <div class="pane pane-code" id="pane-code" hidden>
      <textarea id="code" spellcheck="false" autocomplete="off"
                placeholder="The generated HTML appears here β€” and it's editable. Changes re-render in the preview and are what gets deployed."></textarea>
    </div>

    <!-- ── deploy bar ───────────────────────────────────────────── -->
    <div class="deploybar">
      <button id="deploy" class="btn btn-deploy" type="button" disabled>Deploy to harvis.dev</button>
      <button id="newsite" class="btn btn-ghost btn-sm" type="button" hidden title="Next deploy creates a brand-new site instead of updating this one">New site</button>
      <div class="deployresult" id="deployresult"></div>
    </div>
  </section>
</main>

<div id="toast" class="toast" role="status" aria-live="polite" hidden></div>

<script type="module" src="app.js"></script>
</body>
</html>