7.7 KiB · text History d76cd01
{% extends "root.html" %}

{% block width %}max-w-7xl{% endblock %}

{% block content %}
{# One form drives everything. The preview pane re-posts it to /preview as
   you type; the download and save buttons submit it natively (hx-boost off)
   to different endpoints via formaction. #}
<form id="card-form" method="post" action="/export?format=png" hx-boost="false"
      class="grid grid-cols-1 lg:grid-cols-[20rem_minmax(0,1fr)] gap-6 items-start">

  <aside class="space-y-3 order-2 lg:order-1">
    <div>
      <p class="font-mono text-xs uppercase tracking-[0.25em] text-accent">{{ app_name }}</p>
      <h1 class="text-2xl font-bold">{% if slug %}Edit snippet{% else %}New card{% endif %}</h1>
    </div>

    <label class="form-control">
      <span class="label-text text-xs uppercase tracking-wider opacity-70">Window title</span>
      <input name="title" value="{{ spec.title }}" maxlength="200" class="input input-bordered input-sm w-full font-mono">
    </label>

    <label class="form-control">
      <span class="label-text text-xs uppercase tracking-wider opacity-70">Language</span>
      <select name="language" class="select select-bordered select-sm w-full py-0">
        <option value="auto" {% if spec.language == "auto" %}selected{% endif %}>Auto-detect (from first line)</option>
        {% for l in options.languages %}
        <option value="{{ l.id }}" {% if spec.language == l.id %}selected{% endif %}>{{ l.name }}</option>
        {% endfor %}
      </select>
    </label>

    <label class="form-control">
      <span class="label-text text-xs uppercase tracking-wider opacity-70">Window decoration</span>
      <select name="chrome" class="select select-bordered select-sm w-full py-0">
        {% for c in options.chromes %}
        <option value="{{ c.id }}" {% if spec.chrome == c.id %}selected{% endif %}>{{ c.name }}</option>
        {% endfor %}
      </select>
    </label>

    <label class="form-control">
      <span class="label-text text-xs uppercase tracking-wider opacity-70">Color scheme</span>
      <select name="palette" class="select select-bordered select-sm w-full py-0">
        <option value="auto" {% if spec.palette == "auto" %}selected{% endif %}>Match the decoration's era</option>
        <optgroup label="KDE classics">
          {% for p in options.palettes_kde %}
          <option value="{{ p.id }}" {% if spec.palette == p.id %}selected{% endif %}>{{ p.name }}</option>
          {% endfor %}
        </optgroup>
        <optgroup label="neiam">
          {% for p in options.palettes_neiam %}
          <option value="{{ p.id }}" {% if spec.palette == p.id %}selected{% endif %}>{{ p.name }}</option>
          {% endfor %}
        </optgroup>
      </select>
    </label>

    <label class="form-control">
      <span class="label-text text-xs uppercase tracking-wider opacity-70">Highlighting</span>
      <select name="highlight" class="select select-bordered select-sm w-full py-0">
        {% for t in options.hl_themes %}
        <option value="{{ t.id }}" {% if spec.highlight == t.id %}selected{% endif %}>{{ t.name }}</option>
        {% endfor %}
      </select>
    </label>

    <label class="form-control">
      <span class="label-text text-xs uppercase tracking-wider opacity-70">Padding</span>
      <input type="range" name="padding" min="0" max="{{ max_padding }}" step="4" value="{{ spec.padding }}" class="range range-xs range-primary">
    </label>

    <div class="flex flex-wrap gap-x-5 gap-y-1">
      <label class="label cursor-pointer gap-2 justify-start">
        <input type="checkbox" name="line_numbers" class="checkbox checkbox-sm" {% if spec.line_numbers %}checked{% endif %}>
        <span class="label-text">Line numbers</span>
      </label>
      <label class="label cursor-pointer gap-2 justify-start">
        <input type="checkbox" name="transparent" class="checkbox checkbox-sm" {% if spec.transparent %}checked{% endif %}>
        <span class="label-text">Transparent backdrop</span>
      </label>
    </div>

    <div class="divider my-1"></div>

    <div class="grid grid-cols-2 gap-2">
      <button type="submit" formaction="/export?format=png" class="btn btn-sm btn-outline">
        <i class="fas fa-image fa-fw"></i> PNG
      </button>
      <button type="submit" formaction="/export?format=svg" class="btn btn-sm btn-outline">
        <i class="fas fa-bezier-curve fa-fw"></i> SVG
      </button>
      {% if user %}
        {% if slug %}
          <button type="submit" formaction="/snippets/{{ slug }}" class="btn btn-sm btn-primary">
            <i class="fas fa-floppy-disk fa-fw"></i> Update
          </button>
          <button type="submit" formaction="/snippets" class="btn btn-sm btn-ghost">
            <i class="fas fa-clone fa-fw"></i> Save as new
          </button>
        {% else %}
          <button type="submit" formaction="/snippets" class="btn btn-sm btn-primary col-span-2">
            <i class="fas fa-floppy-disk fa-fw"></i> Save &amp; get embed links
          </button>
        {% endif %}
      {% else %}
        <a href="/login" class="btn btn-sm btn-ghost col-span-2" hx-boost="true">
          <i class="fas fa-right-to-bracket fa-fw"></i> Log in to save &amp; embed
        </a>
      {% endif %}
    </div>
    {% if slug %}
      <a href="/s/{{ slug }}" class="link link-hover text-sm opacity-70" hx-boost="true">&larr; back to the share page</a>
    {% endif %}
  </aside>

  <section class="space-y-4 order-1 lg:order-2 min-w-0">
    {# The named field the form submits. Hidden by default: you edit inside
       the rendered window, which mirrors into this. Not `required`, since a
       hidden required field blocks submission without showing why. #}
    <textarea id="code-source" name="code" rows="14" data-code-input spellcheck="false" autocapitalize="off" autocomplete="off"
              class="textarea textarea-bordered w-full font-mono text-sm leading-relaxed hidden"
              placeholder="Paste some code…">{{ spec.code }}</textarea>

    {# The preview swaps only #preview-inner, so the in-window textarea
       beside it survives re-renders (and keeps focus and caret). That
       textarea has no name: it mirrors the code box above, which is what the
       form actually submits. See initInWindowEditing in f/js/src.js. #}
    <div id="preview"
         hx-post="/preview"
         hx-trigger="input delay:150ms from:#card-form, change from:#card-form"
         hx-sync="this:replace"
         hx-target="#preview-inner"
         hx-select="#preview-inner"
         hx-swap="outerHTML"
         class="relative rounded-box border border-base-300 bg-base-200 p-3 overflow-auto [&_svg]:max-w-full [&_svg]:h-auto [&_svg]:mx-auto [&_svg]:block">
      <div id="preview-inner">{{ card_svg|safe }}</div>
      <textarea data-in-window data-code-input aria-label="Edit the code inside the window"
                spellcheck="false" autocapitalize="off" autocomplete="off" wrap="off"
                class="absolute z-10 m-0 p-0 border-0 bg-transparent text-transparent shadow-none resize-none overflow-hidden whitespace-pre outline-none focus:outline-none focus:ring-0 selection:bg-primary/30"
                style="font-family: 'B612 Mono', monospace; tab-size: 4; -moz-tab-size: 4; left: 0; top: 0; width: 0; height: 0;"></textarea>
    </div>
    <div class="flex flex-wrap items-center justify-between gap-2 text-xs">
      <span class="opacity-60">Click into the window to edit the code in place.</span>
      <button type="button" class="btn btn-xs btn-ghost"
              onclick="const t = document.getElementById('code-source'); t.classList.toggle('hidden'); this.querySelector('span').textContent = t.classList.contains('hidden') ? 'Show plain text box' : 'Hide plain text box';">
        <i class="fas fa-align-left fa-fw"></i> <span>Show plain text box</span>
      </button>
    </div>
  </section>
</form>
{% endblock %}